-------------------------------------------------------------------------------------------------------
- Notes de version de l'addon Sku Anniversary (Français)
-------------------------------------------------------------------------------------------------------
Sku parle français depuis la v42.11 ; ces notes commencent donc à cette version. Pour les
versions antérieures, voir « Patch Notes Sku EN.txt » ou « Patch Notes Sku DE.txt ».
-------------------------------------------------------------------------------------------------------
Changements dans Sku v43.10
Aperçu
Nouveautés
Auras : nouvelle aura de base « Votre cible passe sur un membre du groupe ». Quand l'ennemi que vous ciblez se tourne vers un autre membre, un son est joué et le membre est nommé par son numéro de touche, en groupe comme en raid.
Changements
Détection des rôles : le moniteur de santé détecte maintenant automatiquement tank, soigneur et dégâts aussi pour les membres de raid 26 à 40. Avant, seulement jusqu'à la place 25.
Focalisation : /focus1 à /focus8 acceptent maintenant toutes les places de raid (raid1 à raid40), avant seulement jusqu'à raid25.
Auras : nouvelles valeurs d'unité « membres du groupe ou du raid » et « ... sauf vous ». Elles couvrent tout le raid ; les valeurs de groupe existantes ne couvrent en raid que votre propre sous-groupe.
Corrections
Moniteur de combat : depuis la v43.9, les alertes de menace déclenchaient une erreur Lua (aqCombat.lua:380, UnitGUID) et restaient muettes, par exemple quand on prenait l'aggro en groupe. Corrigé.
Moniteur de combat : les alertes de menace s'entendent à nouveau
Simple : Prendre l'aggro en groupe, ou en être proche, donnait une erreur au lieu de
l'alerte depuis la v43.9. Les alertes « menace haute » et « menace basse » reviennent.
Elles ne nomment aucun joueur et ne l'ont jamais fait ; les notes de la v43.9 le
décrivaient à tort.
Technique : les quatre sorties des deux alertes passaient unit1 = tAllPartyRaidUnits[x]
sans x défini à cet endroit, donc toujours nil ; leurs modèles ne contiennent pas
${unit1}. La v43.9 faisait passer la valeur par tSpokenGroupUnit, qui appelait
UnitGUID(nil). L'argument est retiré, et tSpokenGroupUnit renvoie nil pour nil.
Détection des rôles : places de raid 26 à 40
Simple : Depuis la v43.9 le moniteur de santé du raid annonce aussi les membres 26 à 40,
mais leur rôle n'était pas détecté automatiquement. C'est fait maintenant.
Technique : RoleCheckerIsUnitGUIDInPartyOrRaid ne connaissait que raid1 à raid25 ; la
limite est supprimée. RoleCheckerGetUnitRole plantait quand le membre demandé était
retiré des données de rôle comme ne faisant plus partie du groupe, et sa dernière
ligne lisait une variable hors de portée. Les deux sont corrigés.
Focalisation : les 40 places de raid
Simple : « /focus3 raid30 » enregistrait le texte « raid30 » comme nom, et la touche de
focalisation ne trouvait alors personne. Maintenant c'est le nom du membre qui est
enregistré, comme pour raid1 à raid25.
Technique : tFocusUnitIds (skuFocus.lua) ne connaissait raid, raidpet et leurs chaînes
target que de 1 à 25, maintenant jusqu'à MAX_RAID_MEMBERS.
Auras : aura de base « Votre cible passe sur un membre du groupe »
Simple : Pour les tanks. Sous Nouvelle aura, Auras de base. Quand l'ennemi que vous
ciblez passe sur un autre membre du groupe ou du raid, un son est joué et Sku dit
qui : « party 3 » en groupe (la touche du pavé numérique), le numéro de dial en raid.
Quand il revient sur vous, rien n'est dit. « Qui » permet de restreindre les membres
surveillés. C'est l'aura de l'ancien set du guerrier protection, qu'on ne pouvait
plus charger depuis longtemps, avec une condition plus précise.
Technique : événement UNIT_TARGETCHANGE, source contient target et est attaquable,
destination = la valeur choisie (préréglée raidWoPlayer), sorties son plus unité
cible. L'ancien set ne vérifiait que « la source n'est pas un membre du groupe » ;
comme Sku suit les cibles des 40 places de raid, un soigneur d'un autre sous-groupe
qui sélectionnait quelqu'un la déclenchait aussi. Les auras de base peuvent
maintenant demander autre chose qu'un sort (valueLabel, emptyValueMessage,
defaultValues).
Auras : valeurs pour tout le raid
Simple : Source, cible et « cible de votre cible » ont deux nouvelles valeurs :
« membres du groupe ou du raid » (vous compris) et « membres du groupe ou du raid
sauf vous ». En groupe elles se comportent comme les valeurs de groupe, en raid
elles couvrent tous les membres. Les anciennes valeurs de groupe restent telles
quelles : en raid, seulement votre sous-groupe.
Technique : tEvaluateRaidValue (data.lua) cherche party1-4 et raid1-40 dans l'ensemble
d'unités. « Sauf vous » veut dire : pas de « player » dans l'ensemble, car en raid
GetBestUnitId renvoie aussi votre propre place raidN.
-------------------------------------------------------------------------------------------------------
Changements dans Sku v43.9
Aperçu
Nouveautés
Touches de sous-groupe de raid : Alt+pavé 1 à 8 cible le premier membre du sous-groupe de raid 1 à 8. Si le groupe est vide, Sku dit « Groupe N vide » et rien ne se passe ; hors raid « Pas de raid ». Sous-menu propre « Touches de sous-groupe de raid » dans les raccourcis Sku, désactivable sous Fonctions.
Changements
Auras : les auras de base proposent désormais sous « Sortie » la même liste de sorties que les auras personnalisées, c'est-à-dire tous les sons et toutes les sorties de données (par exemple le nom du sort ou l'unité cible), plusieurs à la fois. Avant, il n'y avait qu'un seul son.
Fenêtres de métier et de maître : après fabrication, apprentissage ou sélection, le menu se rafraîchit en silence, le curseur reste sur la même entrée, et seule une entrée vraiment différente est annoncée.
Moniteur combat : « Annoncer les morts » nomme désormais, en groupe, la touche du pavé numérique du membre mort : « party 5, dead » signifie que le pavé 5 est mort. Cela change un très vieux standard ; le quatrième membre du groupe était annoncé « party 4 ».
Moniteur combat : en raid, « Annoncer les morts » dit le numéro de ciblage au cadran du membre mort, c'est-à-dire la touche ou les deux chiffres qui le ciblent. Avant : « party 2 » pour son propre sous-groupe et « raid 17 » pour tous les autres. Les places de raid 26 à 40 sont désormais annoncées aussi.
Moniteur combat : « Compter les morts » est supprimé.
Moniteur santé : « Ajouter Mort à 0 pour cent » est désormais désactivé par défaut, pour le groupe et le raid, y compris pour les personnages existants.
Ciblage au cadran : uniquement en raid désormais. « Activé » est un interrupteur Activé/Désactivé ; en groupe, les touches 1 à 5 du pavé numérique restent les touches de ciblage normales (1 = soi, 2 à 5 = les autres).
Ciblage au cadran : dans les raids jusqu'à 10 joueurs, toujours une touche par membre (0 = membre 10), deux chiffres à partir de 11 joueurs. L'option « Action à touche unique dans les raids jusqu'à 10 joueurs » disparaît, c'est la règle désormais.
Moniteur santé, raid : les numéros sont les numéros du ciblage au cadran, donc 1 à 10 dans l'ordre des sous-groupes pour les raids jusqu'à 10 joueurs. Avant, toujours le schéma à deux chiffres ; dans un raid de 10 avec quelqu'un dans le groupe 3, la touche 7 s'appelait « 11 ».
Moniteur santé, raid : les membres numérotés 26 à 40 sont désormais prononcés (numéro, puis santé par dizaines) avec le nouveau réglage « Voix pour les membres au-delà de 25 ». Les fichiers de hauteur n'existent que jusqu'à 25, ces membres restaient muets.
Moniteur debuffs, raid : les numéros sont également ceux du ciblage au cadran.
Moniteur combat : les avertissements de menace, « cible de la cible » et « hors de portée » nomment les membres comme l'annonce de mort, avec la touche du pavé numérique en groupe (« party 2 » pour le premier membre) et le numéro du cadran en raid. Avant : la place de raid brute et « party 1 » pour la touche 2 ; les places de raid 26 à 40 manquaient entièrement.
Auras : les sorties « unité source » et « unité cible » nomment aussi les membres du groupe par numéro de touche et les membres du raid par numéro du cadran.
Corrections
Menu des quêtes : « Itinéraire » sous une créature était toujours vide quand son nom contient un trait d'union ou une parenthèse (par exemple « Gorille Crins-de-ciel »). Corrigé.
Fermer l'itinéraire : la recherche des points de passage reliés près d'une cible renvoyait souvent moins que les dix plus proches. N'a très probablement jamais touché personne, correction de pure exactitude.
Auras : « Supprimer toutes les auras » supprimait toutes les auras d'une seule touche, sans demander. Désormais « Vraiment supprimer ? » est demandé d'abord, comme pour la suppression d'une seule aura.
Fenêtres de métier et de maître : après chaque fabrication ou apprentissage, le nom de la fenêtre était annoncé et le curseur sautait parfois sur une autre entrée. Corrigé.
Fenêtres de métier : Entrée sur une recette ou sur « Créer » annonçait l'entrée deux fois. Corrigé.
Boîte aux lettres : à l'ouverture, « Cible » était annoncé avant « Nouveau courrier ». Corrigé.
Raccourcis clavier : la seconde touche (« Réassigner la touche secondaire ») restait sans effet pour plusieurs touches, dont « Déclencher la sortie continue du moniteur de groupe », distance de la cible, mode panique, scan de la minicarte, vérification de portée du groupe, « Tourner vers l'unité » et les touches de scan. Corrigé.
Moniteur combat : « Ignorer les familiers morts du groupe » revenait sur « Oui » après chaque rechargement. Corrigé.
Moniteur debuffs, raid : la sortie continue disait la place de raid plus un, un numéro qui n'appartenait à personne. Corrigé.
Moniteur combat : « party 5, dead » (quatrième membre du groupe, nouveau dans cette version) bipait avec « Numéros seulement » désactivé, car il n'existe pas de mot vocal « party5 ». Désormais « party » puis « 5 » sont dits.
Menu des quêtes : « Itinéraire » trouve les créatures avec un trait d'union dans leur nom
Simple : Dans le menu des quêtes (Début de quête, Cible de quête, Livraison), une créature
propose « Itinéraire », « Fermer l'itinéraire » et « Point de passage ». « Itinéraire »
disait « liste vide » dès que le nom de la créature contenait un trait d'union, une
parenthèse ou un caractère semblable, même quand la créature est bien reliée au réseau
de chemins. Sur un client allemand, cela touchait 249 créatures, dont les trois espèces
de singes du cratère d'Un'Goro. « Fermer l'itinéraire » et « Point de passage »
n'étaient pas concernés.
Technique : SkuQuest/Options.lua, CreateRtWpSubmenu, branche « Itinéraire » :
string.find(i, wpName) sans l'argument plain lisait le nom du point de passage comme un
motif Lua. Là, « - » est un quantificateur (« o- » = un nombre quelconque de o), le nom
ne se trouvait donc jamais lui-même. Désormais string.find(i, wpName, 1, true) aux deux
endroits. Les données d'itinéraire du cratère étaient correctes (vérifié : liens,
indices d'apparition et raccordement au réseau).
Fermer l'itinéraire : les dix points de passage reliés les plus proches sont vraiment trouvés
Simple : La plupart des créatures ne sont pas elles-mêmes reliées au réseau de chemins.
Pour « Fermer l'itinéraire », Sku cherche donc jusqu'à dix points de passage reliés dans
un rayon de 500 mètres autour de la cible et prend le meilleur qui soit accessible.
Cette recherche en renvoyait souvent moins de dix. Le point le plus proche en faisait
pourtant toujours partie, et le réseau de chemins est connexe presque partout. Cela n'a
très probablement jamais touché personne ; la correction n'est là que pour l'exactitude.
Le défaut n'aurait pu se voir que si le point le plus proche se trouve sur un morceau
détaché du réseau : alors « liste vide » ou un petit détour.
Technique : SkuNav:GetNearestWpsWithLinksToWp (SkuNav/Core.lua). L'insertion triée n'avait
pas de cas d'ajout en fin de liste : un candidat plus éloigné que toutes les entrées
était écarté alors que la liste avait encore de la place, et le premier résultat de
pairs() fixait ainsi le plafond. La coupe au-delà de N entrées retirait déjà toujours la
plus éloignée et reste en place. Vérifié hors jeu contre « les N plus proches » (20000
cas aléatoires) : ancien 9059 trop courts, nouveau tous corrects. Les cinq appelants
parcourent toute la liste et prennent la meilleure entrée accessible ; davantage de
candidats ne peut donc donner que des itinéraires aussi bons ou meilleurs.
Auras : les auras de base ont la liste de sorties complète
Simple : Sous « Nouvelle aura », « Auras de base », chaque modèle avait une entrée « Son »
où l'on pouvait choisir exactement un son, rien d'autre. Cette entrée s'appelle
désormais « Sortie », suivie du nombre de sorties activées, et ouvre la même liste
qu'une aura personnalisée : tous les sons et toutes les sorties de données, par exemple
le nom du sort ou l'unité cible. Chaque entrée est un interrupteur, Entrée l'active ou
le désactive, et plusieurs peuvent être actives à la fois. Ce que le modèle imposait
jusqu'ici (son son, plus le nom du sort et l'unité cible pour « Votre amélioration sur
un membre du groupe a expiré ») est déjà activé à l'ouverture et peut maintenant aussi
être désactivé.
Technique : SkuAuras/Options.lua. tBuildOutputsLevel accepte un contexte optionnel
(outputs, ownerLabel, tooltip) ; sans lui, la liste modifie comme avant le brouillon du
constructeur d'auras, avec lui la liste du formulaire de base. Le formulaire
(tBaseForm) tient une liste outputs au lieu d'une seule valeur sound, préremplie à
partir de defaultSound et defaultOutputs (auparavant fixedOutputs, ajoutées d'office à
la création). Le résumé et tBaseFormCommit lisent cette liste ; sans aucune sortie
active, le message reste « Aucune sortie définie ».
Auras : « Supprimer toutes les auras » demande confirmation
Simple : « Supprimer toutes les auras » dans le menu Auras n'avait pas de niveau en
dessous : une touche Entrée ou Flèche droite, et toutes les auras du personnage
avaient disparu. Désormais l'entrée mène à « Vraiment supprimer ? », comme la
suppression d'une seule aura. Seul Entrée à cet endroit supprime, Flèche gauche annule.
Technique : SkuAuras/Options.lua, MenuBuilder : l'entrée est désormais dynamic et son
BuildChildren crée l'unique enfant « Vraiment supprimer ? ». En y entrant, selectTarget
pointe sur l'entrée elle-même, Entrée sur l'enfant appelle donc son OnAction. Avant,
dynamic = false sans enfants, OnPostSelect passait directement dans la branche OnAction.
Menu : les chemins sont résolus au lieu d'être parcourus
Simple : Chaque fois que Sku place lui-même le menu à un endroit donné (une fenêtre s'ouvre,
une touche rapide comme Maj-F10, ou une fenêtre a changé et son menu est reconstruit),
il parcourait jusqu'ici le chemin comme si Entrée était pressée à chaque niveau, en
annonçant chaque étape. Lors de la reconstruction silencieuse d'une fenêtre de métier
ou de maître après une fabrication ou un apprentissage, on entendait donc le nom de la
fenêtre (« Métier », « Maître »), et le curseur était remis par numéro de ligne dans
une liste qui venait de changer. Désormais le chemin cible est résolu directement, sans
annonce des étapes, et le curseur revient sur la même entrée : la même recette (même
avec un nouveau nombre), la même compétence, le même bouton. Ce n'est que si cette
entrée a disparu que l'endroit où le curseur a atterri est annoncé.
Technique : SkuOptions:ResolveMenuPath et EnsureLevelBuilt (SkuZOptions/Core.lua)
remplacent la boucle de SlashFunc qui appelait OnSelect(true) sur chaque segment du
chemin. Les niveaux traversés sont seulement construits (dynamic : RebuildNodeChildren,
sinon un BuildChildren s'ils sont vides), jamais sélectionnés ; seule la destination
reçoit son OnSelect comme avant (préconstruction d'un niveau vide non dynamique, jamais
sur isSkuToggle). Les effets de bord du parcours disparaissent avec lui : les nœuds
actionOnEnter sur le chemin se déclenchaient, tous les frères avant la correspondance
étaient préconstruits. Le paramètre aSilent de SlashFunc est désormais effectif. La
restauration dans SkuCore:CheckFrames fait un appel silencieux au lieu de deux
parlants et place le curseur directement : skuIdentity, puis nom, puis la ligne à
l'ancien index (bornée à la nouvelle longueur). Les entrées de fenêtre portent
skuIdentity (copié par SkuIterateGossipList) : recettes « recipe:Nom (rang) »,
catégories « cat: », compétences du maître « skill: », sinon « frame: » pour
toute entrée seule sur son frame (SkuCore:PrepareWindowEntries à la fin des trois
constructeurs). SkuCore:RefreshWindowMenuQuietly (LocalMenu.lua) est la reconstruction
silencieuse commune pour TRADE_SKILL_UPDATE/CRAFT_UPDATE et le bouton Apprendre ; elle
ne parle que si l'identité (ou le nom sans le nombre) est différente ensuite. Les sacs
gardent leur propre chemin (tFindMenuNodeByPath, tBagAnnounceSuppress).
Fenêtres de métier : Entrée sur une recette ou « Créer » annonçait l'entrée deux fois
Simple : Après Entrée sur une recette ou un bouton Créer, l'entrée était dite deux fois.
Désormais une fois.
Technique : Le chemin de clic classique dans SkuIterateGossipList appelait après func un
CheckFrames parlant puis OnUpdate 0,35 s plus tard, en plus de l'annonce du gestionnaire
de touches. La troisième annonce était le plus souvent absorbée par le garde-doublon
(1 s), la deuxième arrivait 0,9 s plus tard et ne l'était pas. Les entrées avec
quietClick (posé par PrepareWindowEntries pour tout bouton cliquable sans
containerFrameName) passent désormais par func plus RefreshWindowMenuQuietly ; les
boutons à /click sécurisé gardent l'ancien chemin.
Boîte aux lettres : « Cible » avant « Nouveau courrier »
Simple : À l'ouverture d'une boîte aux lettres, « Cible » était annoncé d'abord, puis
seulement « Nouveau courrier ». Désormais seulement « Nouveau courrier ».
Technique : Mail:MAIL_SHOW ouvrait le chemin « Local, Courrier » avant que le MailFrame soit
visible, or l'entrée Courrier sous Local n'existe que lorsque son frame est visible. Le
chemin ne trouvait rien, et l'ouverture silencieuse annonçait à la place la première
entrée racine (« Cible », le menu de cible) ; les sacs ramenaient le menu dans le
courrier un instant plus tard via CheckFrames. Le journal de débogage montrait « path
walk found nothing ... short,lokal,post » à chaque boîte aux lettres depuis au moins la
v43.6 ; cela ne s'est remarqué qu'une fois les annonces intermédiaires disparues.
Désormais MAIL_SHOW ouvre une image plus tard (C_Timer.After(0)), seulement avec un
MailFrame visible et seulement si le curseur n'est pas déjà dans le courrier.
Raccourcis clavier : seconde touche pour le moniteur, le scan, la rotation et les autres
Simple : Sous « Raccourcis clavier Sku », chaque touche a une première et une seconde
assignation. Pour plusieurs touches, la seconde pouvait être définie et était annoncée
comme « Touche 2 », mais ne faisait rien. Étaient concernées « Déclencher la sortie
continue du moniteur de groupe », distance de la cible, mode panique, scan de la
minicarte large et étroit, vérification de portée des membres du groupe, « Tourner vers
l'unité » 1 à 6 et 180 degrés, « Continuer le scan », scan 1 à 8 et la notification de
ressources. La seconde touche fonctionne désormais partout.
Technique : SkuCore/Core.lua, script OnHide de SkuCoreControlOption1 : les assignations de
ce frame étaient armées avec SetOverrideBindingClick pour .key seulement (seule
exception SKU_KEY_TAXICANCEL), alors que le dispatcher de clic vérifie les deux champs
via SkuKeyBindsMatchKey. Les menus d'assignation dans SkuZOptions/Core.lua armaient
key2 depuis toujours, d'où le bon fonctionnement de la seconde touche pour d'autres
actions. Un helper local tArm(aConst) lie désormais chaque touche renvoyée par
SkuKeyBindsGetKeys ; les vingt lignes individuelles écrites à la main disparaissent.
Moniteur combat : l'annonce de mort nomme la touche du pavé numérique
Simple : Sous Moniteur, Combat, Amical, « Annoncer les morts » annonce un membre du groupe
mort. Le numéro qu'elle contient est désormais toujours la touche qui cible ce membre.
En groupe, ce sont les touches par défaut de Sku : pavé 1 = soi, 2 à 5 = les quatre
autres. Le quatrième membre du groupe devient donc « party 5, dead », et non plus
« party 4 ». Le moniteur de santé et l'aperçu ont toujours compté ainsi. En raid, c'est
le numéro de ciblage au cadran qui est dit : la touche unique jusqu'à 10 joueurs, la
séquence à deux chiffres à partir de 11. Avant, on entendait « party 2 » pour les
membres de son propre sous-groupe et « raid 17 » (la place brute du raid) pour tous les
autres, deux numérotations dans un même combat, aucune ne correspondant au pavé. Les
places de raid 26 à 40 n'étaient pas annoncées du tout. « Compter les morts » est
supprimé : en mode « numéro seul », son « 3, dead » ne se distinguait pas de « party 3,
dead », et le compteur ne redescendait jamais lors d'une résurrection.
Technique : SkuCore/aqCombat.lua, aqCombat_SKU_UNIT_DIED. Nouveau helper
tDeathUnitToken(GUID) renvoyant séparément le jeton parlé et l'id d'unité réel, les
vérifications UnitIsDeadOrGhost et familier ont besoin de la place brute. Groupe : jeton
de aqCombatGroupGuidToUnitId, chiffre plus 1. Raid : toutes les places MAX_RAID_MEMBERS
parcourues par GUID (les 25 de tAllPartyRaidUnits ne suffisaient pas), numéro lu dans
la grille du ciblage au cadran (SkuSecureTargetingFrame, unitNameSlotGG-SS, touche =
(GG-1)*5+SS ; du code non sécurisé peut lire les attributs d'un cadre sécurisé), sinon
tDTRaidRoster de aq.lua, sinon calcul direct (sous-groupe-1)*5+position. Grille et
roster ne sont reconstruits qu'hors combat, ils vieillissent donc avec le pavé.
partyDeadCount, son compteur, son menu et sa clé de locale supprimés, la valeur
enregistrée est effacée à la connexion.
Moniteur combat : « Ignorer les familiers morts du groupe » reste sur Non
Simple : Le réglage pouvait être mis sur « Non » mais revenait sur « Oui » après le
rechargement suivant. Corrigé.
Technique : aqCombatOnLogin fixait la valeur avec « x or true » ; un false enregistré est
faux en Lua et était remplacé par true à chaque chargement. Désormais seulement si nil.
Moniteur santé : « Ajouter Mort à 0 pour cent » désactivé par défaut
Simple : Le style hauteur de son du moniteur de santé de groupe et de raid ajoute un clip
« dead » au son d'un membre à 0 pour cent. C'était activé pour le groupe et le raid et
c'est désormais désactivé, y compris pour les personnages existants. Pour le réactiver :
Moniteur, Groupe ou Raid, Santé. L'annonce de mort du moniteur de combat (ci-dessus)
devient ainsi le seul message de mort actif par lui-même.
Technique : SkuCore/aq.lua, AqOnLogin : valeur par défaut false au lieu de true, bascule
unique des personnages existants via addDeadOn0PercentDefaultOff (même schéma que
*DefaultOff dans aqCombat).
Ciblage au cadran : uniquement en raid, une touche jusqu'à 10 joueurs
Simple : « Activé » sous Ciblage au cadran est désormais un interrupteur Activé/Désactivé
et n'agit qu'en raid. Les choix « Groupe » et « Groupe et raid » disparaissent : en
groupe, les touches par défaut de Sku, pavé 1 à 5, atteignent déjà tout le monde, et le
mode groupe posait par-dessus une numérotation décalée (0 = soi, 1 à 4 = les autres) et
masquait les touches par défaut tant qu'il était actif. Un « Groupe » enregistré devient
Désactivé, « Raid » et « Groupe et raid » deviennent Activé. Dans les raids jusqu'à 10
joueurs, une touche par membre est désormais toujours active (pavé 1 à 9, 0 = membre
10, dans l'ordre des sous-groupes), à partir de 11 joueurs la saisie à deux chiffres.
L'option « Action à touche unique dans les raids jusqu'à 10 joueurs » disparaît, c'est
la règle désormais.
Technique : SkuCore/DialTargeting.lua. enabled est converti une fois à la connexion en
Désactivé/Activé, singleKeyinRaid10 effacé. DialTargetingRosterUpdate et
DialTargeting_EndableDisable ne vérifient plus que UnitInRaid et enabled == Activé ; la
branche groupe (unitNameSlot01-0x = partyx) et la branche « party » morte du snippet
sécurisé sont supprimées. Mode raid : tNumCurMembers > 10 signifie deux chiffres, sinon
raid10. Trois clés de locale supprimées. Rien de tout cela n'est testé en jeu.
-------------------------------------------------------------------------------------------------------
Numéros de groupe : un seul comptage pour tous les moniteurs
Simple : Chaque numéro que Sku dit pour un membre du groupe doit être la touche qui le cible.
En groupe, c'est le pavé numérique 1 pour vous et 2 à 5 pour les autres ; en raid, le
numéro du ciblage au cadran, une touche 1 à 10 jusqu'à 10 joueurs et deux chiffres à
partir de 11. L'annonce de mort compte ainsi depuis cette version. Désormais le moniteur
de santé et le moniteur de debuffs en raid, les avertissements de menace, « cible de la
cible » et « hors de portée » du moniteur de combat ainsi que les sorties d'aura
« unité source » et « unité cible » le font aussi. Les moniteurs de santé et de debuffs
du groupe gardent le comptage de groupe en raid, car qui ne surveille que son
sous-groupe le cible là avec le pavé 2 à 5.
Le moniteur de santé en raid n'a des fichiers de hauteur que pour les numéros 1 à 25,
les membres 26 à 40 restaient donc muets. Ils sont désormais prononcés, le numéro
d'abord, puis la santé par dizaines, avec la voix du nouveau réglage « Voix pour les
membres au-delà de 25 » (Moniteur, Raid, Santé ; Justin par défaut).
Technique : SkuCore/aq.lua : SkuCore.Monitor.RaidMemberNumbersByName() construit la table
nom -> numéro selon la règle du cadran (jusqu'à 10 membres à plat 1-10 dans l'ordre des
sous-groupes, à partir de 11 (sous-groupe-1)*5+position) ; tDTRaidRoster et la grille
du ciblage au cadran (DialTargeting.lua) en sont remplis, tDTRaidRoster calculait
toujours à deux chiffres. SkuCore.Monitor.GroupMemberNumber(unitId) renvoie le numéro
de touche des jetons party/raid. La sortie continue des debuffs en raid disait
string.sub(unitId, 5)+1. MonitorOutputRaidPercent2 prononce les numéros au-delà de 25
via MonitorOutputPlayerStatus (raid.health2.voice, 1 par défaut) et renvoie la durée à
la file ; les entrées de la file portent pour cela la santé absolue.
SkuCore/aqCombat.lua : tAllPartyRaidUnits et tUnitsToTestOnGameRaidTargets contenaient
raid1-25 en dur ; ils sont désormais regarnis sur place d'après la taille réelle du
raid à chaque événement de roster. tSpokenGroupUnit(unitId) applique la correspondance
de l'annonce de mort à toutes les annonces amicales. « party5 »/« partypet5 »
n'existent pas comme mots dans les voix de combat, SkuCoreAqCombatGetVoiceString les
découpe en « party », « pet », « 5 ». SkuAuras/data.lua : tUnitIdToSpokenName dit
party N comme N+1 et raid N comme numéro du cadran.
Touches de sous-groupe de raid : sauter dans un groupe de raid d'une seule touche
Simple : Qui ne soigne que son propre groupe en raid a besoin d'un accès rapide à un groupe
sans la saisie à deux chiffres du cadran. Alt+pavé 1 à 8 cible le premier membre du
sous-groupe de raid 1 à 8 (le membre numéro 1 de ce groupe, le même ordre que le
ciblage au cadran et les moniteurs). La cible est annoncée comme toute cible. Si le
groupe est vide, Sku dit « Groupe N vide » et rien ne se passe ; hors raid « Pas de
raid ». Les huit touches ont leur propre sous-menu « Touches de sous-groupe de raid »
dans les raccourcis Sku, la seconde touche fonctionne aussi. La fonction peut être
désactivée sous Fonctions.
Comme pour toute touche de ciblage : les changements de groupe pendant un combat sont
pris en compte après le combat, d'ici là la touche saute vers l'ancien premier membre.
Technique : Nouveau fichier SkuCore/subgroupTargeting.lua (sous-module SubgroupTargeting,
dans la TOC après skuFocus.lua). Huit SecureActionButtons SkuSubgroupTarget1-8 avec
type1=macro, macrotext1="/tar ", liés par SetOverrideBindingClick SANS cinquième
argument (LeftButton, pour que type1/macrotext1 soit lu) ; les noms viennent de
GetRaidRosterInfo (premier nom par sous-groupe dans l'ordre du roster) sur
GROUP_ROSTER_UPDATE, GROUP_FORMED/JOINED/LEFT et PLAYER_ENTERING_WORLD hors combat,
reporté à PLAYER_REGEN_ENABLED en combat. PreClick (non sécurisé, s'exécute aussi en
combat) prononce la ligne vide / pas de raid. Cadre de contrôle SkuCoreSubgroupControl,
script OnHide, arme les deux touches de chaque constante via
SkuOptions:SkuKeyBindsGetKeys. Constantes SKU_KEY_SUBGROUPTARGET1-8, défaut
ALT-NUMPAD1-8 (SkuZOptions/SkuKeyBinds.lua), groupe « Raid Untergruppen Tasten » dans
SkuCore/Options.lua.
Changements dans Sku v43.8
Aperçu
Nouveautés
Réglages du jeu : les cases à cocher avec curseur ou liste déroulante (par exemple « FPS max au premier plan »), les boutons, le bloc « Qualité graphique » et les réglages daltonisme sont maintenant utilisables ; ils disaient jusqu'ici « non pris en charge ».
Réglages du jeu : les valeurs numériques commencent par une entrée « Saisir une valeur » (zone de texte), les chiffres filtrent la liste des valeurs, et les valeurs portent les libellés du jeu (« 60 FPS », « 150% »).
Réglages du jeu : les listes déroulantes disent « (recommandé) » et « (non disponible) » comme le jeu, les lignes grisées « (désactivé) », et les sous-catégories des réglages d'extensions sont affichées.
Changements
Les messages d'erreur du jeu (par exemple « Vous n'avez pas de cible ») ne sont lus qu'une seule fois quand vous martelez une touche. Le même message n'est annoncé de nouveau qu'après avoir laissé la touche tranquille environ deux secondes et demie, ou lorsqu'un autre message d'erreur est arrivé entre-temps.
Se tourner vers la balise, le point de passage ou l'unité : la rotation atterrit désormais avec la même précision sur toutes les machines et est nettement plus rapide. À 20 images par seconde, un demi-tour prenait presque une demi-seconde et un appui sur quatre était perdu ; maintenant un quart de seconde, et presque aucun appui n'est perdu. Sur les machines rapides, les grandes rotations atterrissent à environ un degré au lieu de trois à cinq. L'auto-étalonnage de la v43.7 disparaît.
Corrections
Synthèse vocale : depuis la v43.7, la même ligne se remettait en file à chaque appui et était lue de nombreuses fois de suite, même après une annonce plus importante. Corrigé.
Menu : appuyer sur une touche (Fin, Début, flèche) juste après « menu ouvert » faisait encore lire la première entrée du menu principal (« Menu cible ») après la nouvelle entrée. Corrigé.
Synthèse vocale : une ligne ne se met plus en file derrière elle-même
Simple : Depuis la v43.7, un message déclenché plusieurs fois de suite pouvait entrer dans
la file autant de fois que l'on voulait. En martelant une touche qui provoque une
erreur, on entendait ensuite « Vous n'avez pas de cible » dix ou vingt fois, et après
une annonce de cible intercalée, le message revenait encore une fois. Désormais : si le
même texte attend déjà ou est en train d'être prononcé, la nouvelle copie est écartée.
Les lignes de menu et les autres annonces que vous redemandez expressément avec une
touche ne sont pas concernées.
Technique : SkuVoice:OutputStringBTtts (Libs/SkuVoice-1.0). Le contrôle « est-ce exactement
cela qui est prononcé » s'exécutait à la remise au client. Depuis le verrou client de la
v43.7, une copie n'atteint la remise qu'une fois son prédécesseur FINISHED - le contrôle
ne correspondait plus jamais (journal : file de 35 x la même ligne). Les lignes sans
queuereset sont donc vérifiées à la MISE EN FILE, contre mSkuVoiceQueueBTTS et
mSkuVoiceQueueBTTS_Speaking ; marque de journal « BTTS ENQUEUE-DUP ». Nouveau scénario
de banc d'essai « errspam » (dev/rework-docs/voice-harness).
Menu : la première entrée était encore prononcée en retard après un appui rapide
Simple : À l'ouverture du menu, Sku dit « menu ouvert » puis la première entrée. Si vous
appuyiez sur une touche pendant que « menu ouvert » parlait encore, vous entendiez la
nouvelle entrée - puis quand même « Menu cible », la première entrée, qui n'était plus
la bonne. Cela ne dépendait que de la vitesse de l'appui, pas de la touche ni du menu.
Désormais l'ancienne première entrée est écartée dès qu'une autre entrée de menu est
annoncée.
Technique : Deux causes. (1) La règle de la v43.7 « conserver une ligne en attente sans
réinitialisation derrière la nouvelle ligne d'écrasement » (prévue pour le chat dans
lequel un avertissement s'intercale) s'appliquait aussi aux lignes de menu. La boucle de
conservation de SkuVoice (balayage queuereset, Libs/SkuVoice-1.0) écarte maintenant une
ligne en attente dont la portée est celle de la nouvelle ligne d'écrasement : une
portée est une position dans une interface, seule sa ligne la plus récente est vraie.
(2) Les deux lignes prononcées à l'ouverture (« Menu;open » et SkuOptions.Menu[1].name,
SkuZOptions/Core.lua) appelaient OutputStringBTtts directement SANS la portée « menu »
que VocalizeCurrentMenuName donne à toute autre ligne de menu - la comparaison des
portées n'avait donc rien à comparer. Les deux portent maintenant « menu » ; par
ricochet, EndScope("menu") écarte les deux si le menu est refermé aussitôt. Journal
avant : « BTTS queuereset kept 1 waiting line(s) behind the overwrite ».
Messages d'erreur : annoncés seulement quand ils apparaissent
Simple : Sku suit désormais la règle que le jeu applique pour les voyants. Un message
d'erreur y reste à l'écran environ deux secondes et demie ; si le même message revient
pendant ce temps, il clignote brièvement et reste plus longtemps au lieu d'apparaître
de nouveau. De même, Sku lit un message d'erreur quand il apparaît et se tait tant
qu'il est encore « à l'écran ». Chaque nouvel appui prolonge ce délai. Le clic du jeu
vous indique toujours à chaque appui que cela ne fonctionne pas encore. Si vous laissez
la touche tranquille environ deux secondes et demie puis appuyez de nouveau, le message
est annoncé de nouveau. Un message d'erreur DIFFÉRENT est toujours lu immédiatement,
et après lui le premier compte de nouveau comme nouveau : erreur A, erreur B, erreur A
font trois annonces. Cela ne concerne que les erreurs prononcées, pas les indices
sonores et vocaux.
Technique : UIErrors:OutputError (SkuCore/UIErrors.lua), branche TTS. Seule la DERNIÈRE
erreur prononcée est retenue : tSpokenKey plus tSpokenShownUntil = GetTime() + 2.5
(displayDuration 2 + fadeDuration 0.5 de UIErrorsFrame.xml). Chaque répétition réécrit le
délai, comme ResetMessageFadeByID ; toute autre erreur, y compris un indice sonore ou
vocal, remplace ou efface tSpokenKey. La première version gardait un horodatage PAR
texte - l'erreur A restait donc muette alors que l'erreur B s'était intercalée.
Volontairement un horodatage
et non une lecture de UIErrorsFrame : Blizzard ne limite qu'une vingtaine de types de
messages, pour les autres une ligne s'ajoute à chaque appui, et l'ordre des deux
gestionnaires UI_ERROR_MESSAGE n'est pas défini.
Réglages du jeu : tous les contrôles des réglages Blizzard
Simple : Dans le menu Réglages, Réglages du jeu, beaucoup d'entrées ne disaient que « (non
pris en charge) », par exemple « FPS max au premier plan ». Elles sont maintenant
utilisables. Une case à cocher avec curseur devient deux entrées : l'interrupteur et
« ... valeur ». Les valeurs numériques sont une liste avec les libellés du jeu
(« 60 FPS », « 150% ») ; les chiffres filtrent la liste, et la première entrée « Saisir
une valeur » ouvre la zone de texte. Les listes déroulantes marquent la recommandation
par « (recommandé) » et les valeurs que cette machine ne peut pas exécuter par « (non
disponible) ». Les boutons comme « Réinitialiser la position du chat » exécutent leur
action. Le bloc « Qualité graphique » est un sous-menu avec « Raid et champ de
bataille ». Les raccourcis clavier renvoient au menu propre de Sku. Un réglage que le
jeu grise en ce moment dit « (désactivé) ». De plus, les réglages graphiques, d'affichage
et d'échelle de l'interface n'étaient jusqu'ici que mis en attente et jamais appliqués -
ils prennent effet immédiatement maintenant.
Technique : SkuCore/gameOptions.lua aiguille maintenant selon le frame template de
l'initializer (Blizzard_SettingControls.lua) au lieu du type de valeur : Checkbox,
Slider, Dropdown, CheckboxSlider (cbSetting/sliderSetting), CheckboxDropdown,
CheckboxWithButton, Button, AdvancedQualitySection (ses listes d'options sont des
closures dans le frame ; Sku les dérive d'une table par cvar et interroge
IsGraphicsSettingValueSupported), ColorblindSelector, sections de raccourcis.
setting:SetValue(v) sans second argument ne stocke qu'un pendingValue sur les réglages
portant CommitFlag.Apply, que seul le bouton Appliquer du panneau écrit - Sku n'ouvre
jamais le panneau, d'où SetValue(v, true) maintenant, plus RestartGx / UpdateWindow /
SaveBindings selon le commit flag. GetAllCategories ne renvoie que les catégories de
premier niveau ; GetSubcategories est parcouru récursivement. ShouldShow false = ligne
omise, GetModifyPredicates false = suffixe « (désactivé) ». Les libellés des valeurs
viennent de options.formatters[Label.Right] ; une saisie est reconvertie linéairement
via le formatter (pourcentage, curseur de qualité 1..10). Chaque écriture journalise
« gameOptions: set ». Un harnais hors ligne contre de faux objets Setting a réussi ;
vérifié en jeu : l'interrupteur et la valeur FPS (journal : PROXY_FOREGROUND_FPS = 20,
TurnCal fps 20).
Se tourner vers la balise : exact à l'image près au lieu d'être basé sur le temps
Simple : Jusqu'ici Sku faisait tourner la caméra pendant une durée calculée, puis l'arrêtait.
Or le jeu ne déplace la caméra qu'une fois par image, et un minuteur ne tombe sur la
limite d'image qu'approximativement - à faible cadence, il la rate. Sur une machine lente
(20 images par seconde, reproduit dans le test avec la limite de FPS), chaque rotation de
plus de 15 degrés prenait donc presque une demi-seconde, les petites rotations restaient
trop courtes d'un tiers, un appui sur quatre tombait dans le délai de verrouillage et
était perdu, et à monture on tournait de nouveau autour des points de passage proches.
Désormais Sku compte les images et utilise leur durée réelle : la vitesse de rotation est
choisie pour que l'angle soit un nombre entier de pas d'image, et si le dernier pas
dépassait la cible, il est raccourci. Résultat à 20 images par seconde : un demi-tour en
un quart de seconde, toutes les rotations à moins de deux degrés, presque aucun appui
perdu, plus de cercles. À 80 à 120 images : grandes rotations avec environ un degré
d'erreur au lieu de trois à cinq, petites sous un demi-degré. L'auto-étalonnage de la
v43.7 disparaît - il apprenait des valeurs identiques sur toutes les machines. /skuturn
n'indique plus que si la vitesse rapide fonctionne sur cette machine ; /skuturn reset
réinitialise ce contrôle.
Technique : GameWorldObjects:TurnToWorldPosition (SkuCore/gameWorldObjects.lua). Mesuré sur
environ 400 rotations à 20 et 77 à 125 fps (résidu 0,5 ms) : la caméra avance une fois
par image de vitesse fois la durée RÉELLE de l'image ; l'image dont l'OnUpdate émet
l'arrêt n'avance plus ; la durée d'image mesurée dans un OnUpdate est exactement celle du
pas suivant. Planificateur : n = ceil(angle / (1440 * f)) pas, v = angle / (n * f), CVar
au plus 360, le reste en facteur MoveView. L'arrêt vient d'un compteur OnUpdate qui
additionne les durées d'image réelles et s'arrête dès que la somme atteint le temps de
mouvement angle/v ; si le dernier pas dépassait, cameraYawMoveSpeed est mis à l'échelle
du reste pour cette seule image et l'arrêt suit à l'image suivante - la CVar agit
immédiatement (54 rotations ajustées : erreur 0,5 degré au lieu des 5,3 qu'une CVar
inerte aurait donnés). C_Timer était la cause de l'ancienne dispersion : il ne se
déclenche qu'aux limites d'image et glisse d'une image entière à 2 pour cent de gigue.
Le « glissement » de la caméra de la v43.7 était cette unique image inerte, pas de
l'inertie. L'étalonnage (échelle et latence par vitesse, store v4/v5) apprenait 1,00 et
une image avec une dispersion de 0,01 et a été retiré ; le store turnCal v6 ne garde que
noGear2 (le facteur n'agit pas : médiane vitesse réelle/demandée sous 0,7 sur 6+
rotations rapides). Verrouillage après l'arrêt 2 images ou 50 ms au lieu de 3 ou 100.
Journal « TurnCal » par appui avec n, m, mt, trim, dts (toutes les durées d'image),
fest/fact/fmax, rest_land, lead_shift ; modèle de script d'analyse an4.py dans le
scratchpad. Leçons pour WowVision : dev/rework-docs/TURN-TO-BEACON-LESSONS.md.
-------------------------------------------------------------------------------------------------------
Changements dans Sku v43.7
Aperçu
Changements
Se tourner vers la balise (T), vers l'unité et vers la cible : les grandes rotations arrivent désormais en UN appui. Jusqu'ici chaque appui restait bloqué à environ 88 degrés, un demi-tour en demandait toujours deux.
Les rotations sont nettement plus rapides : un demi-tour prend un bon dixième de seconde sur une machine rapide au lieu d'un quart de seconde.
Si vous êtes déjà face à la balise (à 3 degrés près), vous n'êtes plus tourné. Cela met fin au va-et-vient autour du ping de la balise.
La rotation s'étalonne elle-même : après chaque rotation, Sku vérifie de combien vous avez réellement tourné et s'adapte à votre machine et à votre fréquence d'images. Les toutes premières rotations après la mise à jour peuvent être imprécises.
Écho clavier avec une vraie voix Windows (pas le pont NVDA) : la longue pause après chaque lettre tapée a disparu, les lettres se suivent de près.
Coller du texte dans un champ de saisie (Ctrl+V) : Sku dit désormais une seule fois « Collé » au lieu de lire le texte collé lettre par lettre.
Corrections
Nage et vol : le verrou d'inclinaison s'active désormais aussi en combat. En entrant dans l'eau en plein combat, les premières rotations vous faisaient plonger.
Objets d'un ensemble d'équipement : l'infobulle annonçait deux fois le type d'armure (par exemple « Tissu »). Désormais une seule fois.
Synthèse vocale avec une vraie voix Windows (pas le pont NVDA) : des lignes remplacées depuis longtemps étaient quand même prononcées des secondes ou des minutes plus tard - lettres de filtre tapées après « Menu fermé », entrées de liste sautées après avoir relâché la flèche, discussion de guilde avec une minute de retard. Corrigé.
Menu : quand le menu s'ouvrait directement dans une fenêtre ou une liste (sac, marchand, Maj+F10), « Menu de la cible » était encore annoncé après le contenu. Corrigé.
Vraie voix Windows : si un autre addon parlait pendant la saisie, une lettre pouvait suivre après « annulé ». Corrigé.
Rotation : plus rapide, plus précise, auto-étalonnée
Simple : « Se tourner vers la balise » (T) et les deux autres commandes de rotation ont été
retravaillées à partir de mesures en jeu. Trois choses changent nettement. D'abord : les
grandes rotations arrivent en un appui. Jusqu'ici un appui faisait au mieux environ 88
degrés, quelle que soit la distance à parcourir - un bogue passé inaperçu depuis la
v43.2. Ensuite : la rotation est plus rapide, un demi-tour ne prend plus qu'un bon
dixième de seconde. Enfin : si vous êtes déjà aligné à 3 degrés près, un nouvel appui ne
fait plus rien. Avant, chaque appui tournait d'au moins 5 degrés, on oscillait donc
autour du ping de la balise sans jamais se stabiliser. Dans l'eau, cela évite aussi des
à-coups de plongée inutiles.
Sku apprend votre machine au passage : après chaque rotation, il mesure de combien le
personnage a réellement tourné et calcule la suivante d'après cela. À fréquence d'images
élevée, Sku tourne plus vite de lui-même ; à fréquence basse, plus lentement mais
précisément. Après la mise à jour, les premières rotations sont des étapes
d'apprentissage et peuvent manquer la cible. Les très grandes rotations proches de 180
degrés arrivent à environ 5 degrés près ; de temps en temps un second appui affine. Un
appui pendant une rotation en cours est ignoré comme avant.
Technique : GameWorldObjects:TurnToWorldPosition (SkuCore/gameWorldObjects.lua). Mesuré le
2026-09-21 : cameraYawMoveSpeed n'a plus d'effet au-delà de 360 - des valeurs plus hautes
ne tournent pas plus vite (376, 453 et 731 ont tous donné ~88 degrés en 0,25 s). La
règle max(360, angle/0,25) de la v43.2 plafonnait donc en silence chaque appui à ~88
degrés. La vitesse supplémentaire ne vient que de l'argument de
MoveViewLeftStart/MoveViewRightStart, qui multiplie la CVar (technique de WowVision).
Deux rapports : rapport 1 = CVar seule jusqu'à 360 deg/s, rapport 2 = CVar 360 fois un
facteur jusqu'à 1440 deg/s. Modèle par rapport : tourné = k * vitesse voulue * (durée du
minuteur + L) ; k par la médiane dès la première mesure, puis à partir de 8 mesures aux
durées variées une droite d'ajustement pour k et L. Vitesse selon la fréquence d'images :
la dispersion de l'arrêt vaut une demi-trame fois la vitesse, on autorise 3 degrés ou 6
pour cent de l'angle ; jamais plus lent qu'angle/0,25 s ; ralenti quand le minuteur
serait plus court que 2 trames. Si le facteur ne multiplie pas sur un client, Sku
abandonne définitivement le rapport 2. Mesures par compte dans
SkuOptions.db.global.SkuCore.turnCal. Le verrou contre les appuis suivants couvre
désormais aussi l'attente de mesure (un appui dans cet intervalle calculait avec
l'ancienne orientation). Chaque rotation écrit une ligne « TurnCal » dans le journal de
débogage. /skuturn affiche l'état, /skuturn reset efface les mesures, /skuturn legacy
revient à l'ancien comportement. Construit, testé puis retiré : une correction dans le
même appui - la caméra a de l'élan et continue de glisser après l'arrêt, une commande
dans ce glissement coûtait jusqu'à 35 degrés.
Nage et vol : le verrou d'inclinaison s'active aussi en combat
Simple : Le verrou d'inclinaison automatique, qui vous maintient à l'horizontale en nageant
et en volant, attendait jusqu'ici la fin du combat. En entrant dans l'eau en combattant,
vous restiez sans protection pendant ce temps, et chaque rotation vers la balise vous
enfonçait davantage. Le verrou s'active maintenant immédiatement. La touche Ctrl+Maj+N
fonctionne désormais aussi en combat.
Technique : Les tests InCombatLockdown dans SkuCore:TogglePitchLock et dans le tick
automatique (SkuCore/Core.lua) reposaient sur l'hypothèse non vérifiée que pitchlimit
est protégé en combat. C'est une commande de console, modifiable en combat (testé le
2026-09-21). Dans le journal de ce jour-là, 6 secondes avec trois rotations non
protégées séparaient le début de la nage et le verrou.
Infobulle des objets d'ensemble : le type d'armure était lu deux fois
Simple : Si un objet fait partie de l'un de vos ensembles d'équipement enregistrés, Sku
insère la ligne « Appartient à l'ensemble » dans l'infobulle. Sur ces objets précisément,
le type d'armure s'entendait ensuite deux fois, par exemple « Lié, Tissu, Tête, Tissu ».
Le premier « Tissu » était un reste qui n'avait rien à faire là. Il n'arrive plus
qu'une fois, après l'emplacement : « Lié, Tête, Tissu ». Sur les armes, il en allait de
même pour le type d'arme et la vitesse. Les objets hors ensemble n'étaient pas touchés.
Technique : tInsertSetLineAtTop (SkuCore/equipmentSets.lua) décale d'un cran vers le bas
toutes les lignes de l'infobulle à partir de la ligne 2. La colonne de droite n'était
écrite que si la ligne source en avait une. La ligne que « Tête | Tissu » quittait
recevait « Lié » à gauche et gardait son ancien « Tissu » à droite. TooltipLines_helper
lit chaque région FontString séparément, d'où le reste audible. La colonne de droite
est maintenant toujours écrite : renseignée et affichée, ou vidée et masquée ; de même
pour la ligne 2. Le défaut était dans le code depuis la v42.00 et n'est apparu
qu'avec des ensembles enregistrés.
Synthèse vocale : des lignes remplacées étaient prononcées plus tard (vraies voix Windows)
Simple : Cela ne concerne que les joueurs qui font parler Sku avec une vraie voix Windows,
pas avec le pont NVDA. Là, une ligne que Sku avait remplacée depuis longtemps pouvait
rester coincée dans le jeu et être prononcée bien plus tard : en tapant vite un filtre
dans un menu, on entendait « g r » ou « g r e i f » seulement après « Menu fermé ». En
maintenant la flèche, on entendait deux ou trois entrées sautées après la dernière. Et
les lignes de discussion arrivées pendant le défilement venaient jusqu'à 70 secondes
trop tard et dans le désordre. Dans un journal envoyé par un joueur, cela faisait 24
annonces sur 524 en une demi-heure. C'est corrigé : ce que Sku a remplacé ou annulé
reste muet. Les règles sur ce qui peut interrompre quoi ne changent pas. Le mélange
reste aussi : quand une annonce importante s'insère dans une série de lignes en attente
(discussion, messages d'état), elle est prononcée d'abord, puis la série continue, la
plus ancienne d'abord. Seul ce qui a alors plus de 30 secondes est abandonné.
Technique : Libs/SkuVoice-1.0. C_VoiceChat.StopSpeakingText n'arrête que l'énoncé EN COURS
de lecture. Il n'atteint pas un énoncé transmis mais pas encore démarré, et tout ce qui
est transmis pendant qu'un autre énoncé est encore dans le client y est mis en attente
et ne démarre qu'au prochain PLAYBACK_FINISHED naturel - quel que soit le nombre
d'arrêts entre-temps. Nouveau : une porte commune (mClientGate) pour TOUTES les
transmissions. Sku ne donne au client qu'UN énoncé à la fois ; la ligne suivante attend
dans la file de Sku le FINISHED/FAILED de l'identifiant correspondant, là où queuereset,
EndScope et CancelBttsOutput peuvent encore l'atteindre. Comme les lignes attendent
désormais dans Sku, la règle du queuereset que le client fournissait par hasard a dû
être énoncée : une ligne d'écrasement en attente est dépassée et supprimée
(mSkuVoiceQueueBTTS_Overwrite) ; une ligne en attente sans queuereset est placée
derrière la nouvelle ligne d'écrasement tant qu'elle a moins de tBttsKeptMaxAge
(30 s). Les cinq points d'arrêt passent
par BttsStop : si l'énoncé dans le client n'a pas démarré, l'arrêt est noté et délivré
0,1 s après son STARTED, depuis OnUpdate - jamais depuis le gestionnaire STARTED
lui-même : un StopSpeakingText émis de là a bloqué toute la synthèse vocale du client
jusqu'au redémarrage lors des tests (l'ancienne fenêtre d'élimination faisait cela). Cela remplace la fenêtre d'élimination de la v43.5 (la même idée avec une
durée estimée, câblée à deux endroits seulement) et la porte propre à l'écho de frappe.
Garde-fous : 0,5 s sans STARTED ouvre la porte (et envoie quand même un arrêt noté) ;
après trois transmissions sans aucun STARTED, la porte se désactive jusqu'au prochain.
Avec le pont NVDA, STARTED et FINISHED arrivent dans l'image de la transmission : la
porte n'y est jamais fermée - comportement inchangé. Vérifié avec un modèle hors ligne
du client (dev/rework-docs/voice-harness) : ancien code 27-33 énoncés en retard en
3 minutes de charge aléatoire, nouveau code 0 ; modèle du pont identique avant et après.
Dans le journal : "stop deferred (handover not started)", "deferred stop fires",
"client gate ttl", "queuereset kept", "STALE-DROP".
Bogue induit, corrigé au passage : la touche « arrêter la sortie audio » (Ctrl+V)
n'annulait plus que la ligne en cours, les lignes en attente continuaient - un appui
par ligne. Avant, toute la rafale se trouvait déjà dans le client et l'unique arrêt
touchait tout ; depuis la porte, les lignes attendent chez Sku, et
StopOutputEmptyQueue ne vidait jamais cette file. StopOutputEmptyQueue(true, true)
appelle maintenant CancelBttsOutput : lignes et caractères tapés en attente supprimés,
puis l'arrêt. Les appelants avec (true, nil) restent tels quels - les lignes de chat
en attente doivent y survivre. Scénario du banc d'essai « stopkey » : ancienne version
quatre lignes audibles, nouvelle version une seule (coupée).
Menu : « Menu de la cible » était annoncé après le contenu
Simple : Quand le menu ne s'ouvre pas à sa racine mais directement à un endroit précis -
ouverture d'un sac, d'un marchand ou d'une boîte aux lettres, Maj+F9 et Maj+F10, les
touches de sélection rapide -, on entendait encore, après le contenu, le premier
élément de la racine du menu, « Menu de la cible ». Par exemple « Sac 1, Menu de la
cible ». C'est corrigé : vous n'entendez plus que l'endroit où vous arrivez. « Menu
ouvert » n'est plus prononcé pour ces ouvertures ; il était de toute façon coupé
aussitôt, et le son d'ouverture reste. L'ouverture normale du menu est inchangée :
« menu ouvert », puis « Menu de la cible ».
Technique : SkuOptions:SlashFunc (SkuZOptions/Core.lua) ouvre un menu fermé via le OnClick
de OnSkuOptionsMain puis parcourt le chemin. La branche d'ouverture prononçait « Menu
ouvert » en overwrite et plaçait Menu[1].name derrière comme ligne en attente.
L'annonce du parcours posait un queuereset, et depuis la porte client de cette version
un queuereset conserve les lignes en attente sans overwrite derrière la nouvelle ligne
- prévu pour le chat, ici cela touchait l'élément racine (journal : "queuereset kept 1
waiting line(s)"). Corrigé à la source plutôt que par suppression : avec un chemin (au
moins deux champs), SlashFunc pose SkuOptions.tOpeningForPathWalk autour de l'appel
d'ouverture, et la branche d'ouverture ne dit alors rien. Le drapeau est effacé juste
après l'appel, même en cas d'erreur (pcall, erreur relancée). Si le chemin ne trouve
rien et que le menu reste à la racine, SlashFunc rattrape l'annonce et écrit "menu:
path walk found nothing" dans le journal de débogage. Les étapes intermédiaires du
chemin étaient déjà muettes auparavant.
Écho clavier avec une vraie voix Windows : la longue pause entre les lettres a disparu
Simple : Avec une vraie voix Windows (pas le pont NVDA), la saisie dans le chat laissait une
pause d'une demi-seconde à une seconde après chaque lettre - environ une lettre par
seconde, quelle que soit la vitesse de la voix. Une voix plus lente allongeait
seulement la lettre, la pause restait la même. Cette pause a disparu, les lettres se
suivent de près. Chaque lettre est toujours prononcée, y compris les lettres doublées,
et plus aucune lettre ne suit après Échap. La même pause existait entre des lignes en
attente l'une derrière l'autre (parties d'un texte de quête, plusieurs lignes de
chat) ; elle s'y remarque à peine, mais elle est raccourcie elle aussi. Rien ne change
avec le pont NVDA.
Technique : Le client signale VOICE_CHAT_TTS_PLAYBACK_FINISHED de façon fixe 0,5 à 1 s APRÈS
la fin du son (signet « End ») et ne démarre rien avant. Transmettre plus tôt n'aide pas
(v43.5e : l'énoncé est seulement mis en attente et ne peut plus être annulé). Ce qui
libère le client immédiatement, c'est un arrêt. Nouveau dans Libs/SkuVoice-1.0 : dès
que « Start » puis « End » sont arrivés pour notre énoncé et que quelque chose attend
(voie d'écho ou file), OnUpdate envoie un StopSpeakingText après tTailCutDelay (0,03 s)
et transmet après tTailCutHold (0,06 s). Jamais depuis le gestionnaire de signets. Un
« End » avant « Start » (arrive sur le premier énoncé après un arrêt) ne compte pas. Si
rien n'attend, la ligne se termine d'elle-même comme avant. La coupe ne pose pas la
marque du pont, bloque une fois le contournement « #queue > 1 » et vide elle-même
l'entrée du garde anti-doublon (un énoncé arrêté ne signale pas FINISHED). Mesuré dans
le modèle hors ligne : cycle par lettre 1,15 s -> 0,73 s. Pour essayer, session
uniquement : /skudebug tts tail , /skudebug tts tail off. Dans le
journal : « tail cut -> StopSpeakingText ».
La parole d'autres addons perturbait l'écho clavier
Simple : Si un autre addon (ou une commande /run) parlait via la synthèse vocale du jeu
pendant que vous tapiez, une lettre pouvait encore suivre après « annulé », donc après
la fermeture du champ de chat. Uniquement avec une vraie voix Windows. Corrigé : tant
qu'une parole étrangère est en cours, Sku retient ses lettres et ses lignes de chat ;
si vous fermez le champ, les lettres en attente sont abandonnées. Les annonces qui
interrompent tout de toute façon (menu, « annulé », lignes prioritaires) interrompent
aussi la parole étrangère immédiatement, comme avant.
Technique : La porte du client ne connaissait que les transmissions de Sku. Un énoncé
étranger (capture : id 403, en attente, démarré 11 s plus tard sous le premier
caractère tapé) laissait le caractère suivant sans STARTED, le TTL de 0,5 s
l'abandonnait, un deuxième entrait dans le client et prenait le STARTED du premier
pour le sien. Nouveau (mForeign, tMaxSeenId, tClientGateFreedAt) : un STARTED que
personne n'attend, ou dont l'id est inférieur au plus élevé vu avant notre
transmission, est étranger - jamais adopté comme notre id, et rien n'est transmis tant
qu'il joue (plafond de silence 8 s ; nos propres arrêts y mettent fin). Sur le pont
tous les id valent 0, « plus ancien » est donc strictement inférieur et ne s'y
déclenche jamais. Dans l'image où l'un de nos énoncés signale FINISHED, rien n'est
transmis : un énoncé étranger en attente démarre exactement sur ce FINISHED et doit
être vu d'abord. Scénario du banc d'essai « foreign » : ancienne version quatre
lettres après « annulé », nouvelle version aucune. Dans le journal : « BTTS foreign
utterance », « BTTS foreign cleared ».
Coller dans un champ de saisie : « Collé » au lieu d'une suite de lettres
Simple : Si vous colliez du texte avec Ctrl+V dans la ligne de chat ou dans un champ de
saisie de Sku, tout le texte collé était lu lettre par lettre. Sku dit maintenant une
seule fois « Collé ». Lisez le contenu du champ comme d'habitude avec flèche haut ou
flèche bas. La frappe normale est inchangée, même rapide. Si vous ne collez qu'un ou
deux caractères, ils sont toujours prononcés comme des caractères.
Technique : Un collage livre le presse-papiers sous forme d'appels OnChar individuels,
tous dans la même image. tSpeakTypedChar (SkuZOptions/Core.lua) compte les caractères
par image (GetTime() est constant dans une image) : à partir du troisième, la suite
compte comme un collage - CancelEcho supprime les caractères déjà en attente (aucun
n'a encore été transmis dans cette image, la pompe tourne dans OnUpdate), puis un seul
SpeakEcho(« Collé »), tout autre caractère de l'image est ignoré. Seuil 3, car deux
frappes peuvent tomber dans une même image à faible fréquence d'images. Vaut pour
AttachInputEcho (chat) et le champ de saisie de Sku. Dans le journal : « inputEcho
paste erkannt ».
-------------------------------------------------------------------------------------------------------
Changements dans Sku v43.6
Aperçu
Changements
Fenêtres de métier : la liste des recettes montre maintenant tout le métier avec des catégories à replier et déplier, au lieu d'une liste défilante de 8 entrées.
Fenêtres de métier : Page précédente et Page suivante sautent de catégorie en catégorie.
Fenêtres de métier : le filtre « matériaux disponibles » est un interrupteur normal et ne mène plus à des pages vides.
Corrections
Chat : « expéditeur du chuchotement » et « envoyer le message au canal » dans le menu de ligne (Ctrl+Entrée sur une ligne de chat) ne faisaient rien du tout sur certaines machines - la ligne de chat ne s'ouvrait pas.
Chuchoter depuis le menu de la cible et depuis le navigateur de donjons ne faisait rien non plus sur ces mêmes machines.
Touches : si vous aviez lancé la v43 une fois, étiez revenu à la v42 et y aviez réattribué Maj+F9 à F12, la v43 vous laissait sans touche pour la liste des points de passage, les destinations d'itinéraire, les barres d'action et « terminer l'itinéraire » - les touches sont maintenant reprises après coup.
Classic Era : la liste des sorts non interruptibles s'arrêtait sur une erreur au chargement ; elle se charge maintenant sur tous les clients.
Fenêtres de métier : l'infobulle d'une recette (Maj+Flèche bas) omettait le nom de certains composants, on n'entendait que des chiffres comme « 0 2 ».
Chat : « expéditeur du chuchotement » n'ouvrait pas de ligne de chat
Simple : Si vous ouvriez le menu de ligne dans le chat avec Ctrl+Entrée et choisissiez
« expéditeur du chuchotement », il ne se passait rien sur certaines machines : aucun son,
aucune ligne de chat au nom de l'expéditeur. Il en allait de même pour « envoyer le
message au canal » et « envoyer le lien de l'objet au canal ». Copier la ligne de chat
et copier le nom de l'expéditeur fonctionnaient toujours, car ils passent par un autre
chemin. Les trois entrées ouvrent de nouveau la ligne de chat, et le canal cible est
annoncé comme d'habitude (« À Kai »).
Technique : SkuChat:SetEditboxToCustom appelait le global ChatEdit_UpdateHeader et n'affichait
la zone de saisie qu'APRÈS. Sur le client 2.5.6, ce nom n'est plus une vraie fonction
mais un alias de Blizzard_DeprecatedChatInfo (Deprecated_ChatFrame.lua), et tout ce
fichier s'arrête aussitôt quand la CVar loadDeprecationFallbacks est désactivée. Le nom
vaut alors nil, l'appel lève une erreur, et Show() et SetFocus() ne s'exécutent jamais.
Nouveau : SkuChat:UpdateEditboxHeader appelle la vraie méthode du cadre
ChatFrame1EditBox:UpdateHeader() et ne garde le nom global qu'en secours.
SkuChat_CanAddChannel lit Constants.ChatFrameConstants.MaxChatChannels au lieu de
l'ancien MAX_WOW_CHAT_CHANNELS.
Chuchoter depuis le menu de la cible et le navigateur de donjons ne faisait rien
Simple : « Chuchoter » dans le menu de la cible actuelle, et pour un chef de groupe dans le
navigateur de donjons, n'ouvrait aucune ligne de chat sur ces mêmes machines - sans le
moindre message. Les deux fonctionnent de nouveau.
Technique : Les deux endroits appelaient ChatFrame_OpenChat derrière un test « s'il existe ».
Ce n'est là aussi qu'un alias de Blizzard_DeprecatedChatInfo ; s'il manque, il ne se
passait rien, en silence. ChatFrameUtil.OpenChat est utilisé maintenant, l'ancien nom
seulement en secours (SkuMob/Options.lua, SkuCore/dungeonBrowser.lua).
Touches : Maj+F9 à F12 ne menaient nulle part après un passage v43 - v42 - v43
Simple : Étaient concernés ceux qui avaient lancé la v43 une fois, étaient revenus à la v42 et
y avaient remis les accès rapides 1 à 4 sur Maj+F9 à F12. Au lancement suivant de la v43,
les actions fixes (liste des points de passage, destinations d'itinéraire, barres
d'action, terminer l'itinéraire ou le point de passage) n'avaient plus de touche, et les
quatre touches appelaient à la place d'anciens chemins d'accès rapide. L'un d'eux pointait
vers une entrée de menu qui n'existe plus : le menu s'ouvrait et le curseur restait sur
le premier élément - exactement comme l'ancien blocage « les points de passage sont encore
en cours de chargement ». Sku reconnaît maintenant cet état au démarrage et reporte de
lui-même les quatre touches sur les actions fixes. Vos propres touches sur les accès
rapides ne sont pas touchées. Merci à Kai pour son analyse précise (issue 7).
Technique : tMigrateQuickKeys (SkuZOptions/SkuKeyBinds.lua) prenait la seule présence de
SKU_KEY_NAVWAYPOINTSQUICK pour « déjà migré ». La v43 avait créé l'entrée à son premier
lancement, la v42 l'a vidée en attribuant les touches à MENUQUICK1 à 4 - présente, mais
vide. Nouveau : une passe de rattrapage. Si NAVWAYPOINTSQUICK, NAVROUTEDESTINATIONSQUICK
et ACTIONBARSOPEN sont toutes les trois vides ET qu'au moins un MENUQUICK1 à 4 porte une
des touches par défaut de la v42, SHIFT-F9 à SHIFT-F12, la reprise s'exécute encore une
fois - dans cette passe uniquement pour les emplacements qui portent une telle touche par
défaut. Qui a vidé exprès les nouvelles touches garde ainsi chacune de ses propres touches
d'accès rapide. La passe ne s'exécute qu'une fois : ensuite les nouvelles entrées ne sont
plus vides.
Classic Era : la liste des sorts non interruptibles ne se chargeait pas
Simple : Sur Classic Era, un fichier de données de Sku s'arrêtait sur une erreur Lua à la
connexion. Sku se retrouvait sans la liste dont se sert « N'annoncer que les sorts
interruptibles ». Le fichier se charge maintenant entièrement sur tous les clients. Rien
ne change sur TBC Anniversary.
Technique : SkuDB/uninterruptibleCasts.lua construit ses clés de nom avec GetSpellInfo(id)
directement dans le constructeur de table. Era ne connaît pas les sorts TBC 29121 (Tir à
l'arc) et 33808 (Tir avec une arme à feu), GetSpellInfo renvoie nil, et « [nil] = true »
lève « table index is nil » - le constructeur s'interrompt,
SkuDB.uninterruptibleCasts.spells et .npcSpells restent nil. Le fichier utilise maintenant
une enveloppe locale qui renvoie, pour un identifiant inconnu, une valeur de remplacement
qu'aucun nom de sort ne peut égaler. L'entrée est sans effet sur ce client, les
constructeurs restent identiques ligne à ligne à la source amont (ClassicCastbars), et la
même protection couvre les lignes npcSpells, où un nil aurait levé une erreur de
concaténation.
Fenêtres de métier retravaillées : catégories repliables au lieu de 8 entrées par page
Simple : Les fenêtres de métier (tous les métiers de fabrication, l'enchantement et le
dressage des familiers) se comportent maintenant de façon cohérente. Jusqu'ici, le menu
ne montrait que les 8 lignes affichées par la fenêtre de Blizzard, plus « défiler vers le
haut » et « défiler vers le bas ». Désormais tout le métier tient dans une seule liste :
chaque catégorie comme titre, ses recettes en dessous. Entrée sur une catégorie la replie
ou la déplie, le nouvel état est annoncé, et Sku le mémorise par personnage et par
métier. Page suivante et Page précédente sautent à la catégorie suivante ou précédente.
« Sélectionné » et les boutons de création restent à la fin de la fenêtre. Si deux
catégories portent le même nom (la couture a deux fois « Tissu »), la seconde s'appelle
« Tissu 2 ». Le filtre « matériaux disponibles » est maintenant un interrupteur normal :
Entrée le bascule et le curseur reste en place. Avec le filtre, les recettes sans
matériaux disparaissent, ainsi que les catégories où rien n'est fabricable. On n'arrive
plus sur des pages vides sans issue, et l'enchantement n'a plus de pages où il ne reste
que les titres. La liste se met à jour toute seule après une fabrication et ne parle que
si l'entrée sous le curseur a disparu.
Technique : Build_TradeSkillFrame et Build_CraftFrame (SkuCore/LocalMenu.lua) lisent la liste
via GetTradeSkillInfo / GetCraftInfo au lieu de relever les lignes TradeSkillSkill1-8 et
Craft1-8 ; la sélection passe par TradeSkillFrame_SetSelection /
CraftFrame_SetSelection. TradeSkillOnlyShowMakeable de Blizzard n'est plus utilisé : il
ne remet le défilement à zéro que si l'index de SÉLECTION sort de la liste raccourcie -
sinon le décalage restait derrière la fin de la liste, et les 8 lignes ainsi que la barre
de défilement avaient disparu. Le filtre vérifie maintenant numAvailable dans les deux
fenêtres ; l'ancien cache resFilterApplied (par métier, alors que l'interrupteur de
Blizzard est global) est supprimé. Nouveau : TRADE_SKILL_UPDATE et CRAFT_UPDATE
reconstruisent le menu, avec anti-rebond et seulement si la liste affichée a changé
(SkuCore:RefreshProfessionMenu). Le constructeur de listes de fenêtre connaît deux
nouveaux champs : « toggle » (un interrupteur MakeToggleNode dans une fenêtre) et
« isSectionHeader » (cible de saut pour Page précédente/suivante). L'état du filtre est
maintenant stocké sous le nom du métier au lieu du titre de la fenêtre ; une ancienne
valeur est reprise.
Fenêtres de métier : l'infobulle d'une recette citait certains composants sans nom
Simple : Dans l'infobulle d'une recette (Maj+Flèche bas), quelques composants n'affichaient que
la quantité, par exemple « 0 2 » au lieu de « Eau de Javel 0 sur 2 ». Cela touchait les
objets que le client du jeu n'a encore jamais vus - surtout des articles vendus uniquement
par les marchands, comme l'eau de Javel, les teintures ou le fondant. Les étoffes et les
cuirs sont presque toujours déjà connus du client, d'où l'impression de hasard. Sku dit
maintenant « chargement en cours » à cet endroit et récupère le nom en arrière-plan.
Fermez l'infobulle et appuyez de nouveau sur Maj+Flèche bas : le nom est là. Cela n'arrive
qu'une fois par objet, ensuite le client le connaît durablement.
Technique : GetTradeSkillReagentInfo / GetCraftReagentInfo ne renvoient aucun nom pour un objet
absent du cache d'objets, et le lien du composant porte alors des crochets vides « [] ».
Le repli écrivait un « ? », que la synthèse vocale ne prononce pas. De plus, le texte
fini était rangé dans textFull du nœud de menu et jamais reconstruit : le défaut restait
même après le chargement de l'objet. Nouveau (SkuZOptions/Core.lua, SHIFT-DOWN) : sans
nom, l'identifiant est lu dans le lien et GetItemInfo(id) est appelé, ce qui demande aussi
l'objet au serveur. Une infobulle incomplète est marquée skuRecipeIncomplete et
reconstruite à la prochaine pression. Chaque cas écrit une ligne « recipeTooltip » dans
le journal de débogage (index de recette, composant, lien).
-------------------------------------------------------------------------------------------------------
Changements dans Sku v43.5-tools - installateur 5.2 et outil de connexion 3.4, pas de nouvelle version de l'addon
Aperçu
Nouveautés
Outil de connexion 3.4 : le contrat social de Blizzard est reconnu, défilé jusqu'à la fin et accepté - jusqu'ici, un nouveau joueur aveugle restait bloqué devant.
Outil de connexion 3.4 : l'avertissement JcJ affiché à la création d'un personnage sur un royaume JcJ est lu, et l'on répond avec Entrée ou Échap.
Changements
Outil de connexion 3.4 : les avertissements hardcore et JcJ sont trouvés à toutes les résolutions et tailles de fenêtre, et plus seulement sur les grands écrans.
Outil de connexion 3.4 : chaque voix est essayée de façon inaudible avant d'être acceptée - une voix défectueuse ne peut plus rendre l'outil muet.
Installateur 5.2 : le paquet de journaux contient désormais la liste de toutes les voix Windows (voices.txt).
Installateur 5.2 : mesures contre les fausses alertes de Windows Defender - le fichier de l'installateur indique son produit et son auteur, et le fichier du pont NVDA fourni n'a plus de signature de développeur résiduelle.
Corrections
Outil de connexion 3.4 : « Séléctionner la voix » était vide sur certaines machines, et la première configuration n'allait jamais plus loin.
Outil de connexion 3.4 : le contrat social de Blizzard est accepté automatiquement
Simple : Les nouveaux comptes - et tous les autres dès que Blizzard modifie le texte -
voient un « contrat social » sur l'écran de sélection du personnage. La fenêtre avale
toutes les touches, « Accepter » reste verrouillé tant que le texte n'a pas été défilé
jusqu'au bout à la souris, et « Refuser » quitte le jeu. L'outil restait muet devant,
ou disait au mieux « écran inconnu ». Il annonce maintenant que le contrat social est
ouvert, fait défiler le texte jusqu'à la fin, appuie sur Accepter, vérifie que la
fenêtre a disparu, puis construit la liste des personnages comme d'habitude. Il en va
de même si le contrat réapparaît en cours de session. En cas d'échec, l'outil dit ce
qu'une personne voyante doit faire une fois, et réessaie après une minute. Refuser
n'est jamais pressé.
Technique : La fenêtre est SocialContractFrame (Blizzard_GlueXML\SocialContract.xml/.lua),
déclenchée par le SERVEUR (C_SocialContractGlue.GetShouldShowSocialContract ->
SOCIAL_CONTRACT_STATUS_UPDATE) ; aucune CVar ni aucun réglage n'existe, on ne peut
donc ni la provoquer ni y répondre à l'avance. L'ancien code ne pouvait pas
fonctionner sur le client Anniversary : le test de l'assistant exige le logo jaune
assombri, que l'écran BC n'a pas, et son troisième point de mesure tombe au milieu du
texte du contrat - test faux, écran « unknown », l'outil n'entrait jamais en mode
connexion ; la partie clic était un balayage aveugle de 34 clics. Nouveau : huit
points Sc* dans data.ini (déduits du XML : cadre 510x632, boutons à ui y 706..728,
PAS un DefaultScaleFrame), plus IsSocialContract, AcceptContract et
HandleSocialContract dans flows.ahk et deux branches CheckMode dans menu.ahk - sans
recompiler l'assistant. Vérifié avec /validate, zéro faux positif sur toutes les
captures de référence et détection sur des images synthétiques du contrat à trois
résolutions. Pas encore testé sur la vraie fenêtre, qu'on ne peut pas faire
apparaître sur commande ; chaque tentative écrit ses mesures dans log.txt (lignes
« SocialContract: »).
Outil de connexion 3.4 : avertissements hardcore et JcJ à toutes les résolutions, avertissement JcJ de création nouveau
Simple : L'avertissement hardcore de la liste des royaumes, les règles hardcore à la
création d'un personnage et les avertissements JcJ sont des fenêtres que Blizzard
garde à la même taille en pixels réels. Leur position dépend donc de la résolution, et
les points de mesure fixes de l'outil n'étaient justes que sur les grands écrans (à
partir de 1200 pixels de haut). Sur un écran 1080p courant, ou dans une petite
fenêtre, l'outil serait resté muet devant les règles hardcore. Il cherche maintenant
ces fenêtres lui-même et les trouve à toutes les résolutions. S'y ajoute
l'avertissement JcJ à la création d'un personnage sur un royaume JcJ : il est lu,
Entrée accepte, Échap refuse. L'avertissement JcJ de la liste des royaumes a
exactement l'aspect de l'avertissement hardcore et était donc déjà lu.
Technique : HardcorePopUpFrame (240 et 580 de haut) et RealmWarningPopUpFrame (240 et 360
de haut) héritent de DefaultScaleFrame et sont mis à l'échelle par GetDefaultScale() -
une fonction du client sans source Lua. Mesuré en jeu avec /wdeval : 0,64 en
2880x1800, là où 768/hauteur donnerait 0,43 ; il existe donc un plancher, et le 1080p
donnerait sans doute 0,71. Au lieu de supposer l'échelle, GlueDialogScan (flows.ahk)
la trouve : dans les quatre formes, les deux boutons sont sur une droite partant du
centre de l'écran, et seule la distance dépend de l'échelle. Une capture (nouvelle
classe ScreenGrab dans ui.ahk, 1000 pixels en 16 ms), puis échelle 0,60..1,25 par
hauteur : rouge sur trois colonnes, pas de rouge dans l'interstice ni juste à
l'extérieur, noir au point de fond. Les points fixes restent le premier test, le
balayage le second. Les clics (GlueDialogClick) et les zones de texte OCR
(GlueDialogBand) suivent l'échelle trouvée. Vérifié avec un portage Python sur 66
fenêtres synthétiques (6 résolutions, 3 hauteurs, 4 échelles) : hauteur toujours
juste, échelle à 0,012 près, zéro faux positif. Pas encore testé sur de vraies
fenêtres ; chaque détection est consignée dans log.txt (ligne « GlueDialog: » avec
l'échelle).
Outil de connexion 3.4 : sélection de voix vide corrigée, la première configuration ne reste plus bloquée
Simple : Sur certaines machines, Windows ne peut pas fournir la liste des voix installées
alors que la synthèse vocale elle-même fonctionne - par exemple après une voix tierce
mal désinstallée ou à moitié installée. Depuis la 3.3, l'outil ne plante plus dans ce
cas, mais « Séléctionner la voix » était alors vide : la flèche droite ne faisait rien,
et comme la première configuration ne passe à la langue qu'après le choix d'une voix, on
ne la traversait jamais. L'outil lit désormais lui-même les voix dans le registre
Windows dans ce cas. S'il n'en trouve pas non plus ainsi, le menu contient l'entrée «
Aucune voix trouvée, appuyez sur Entrée pour garder la voix du système » - Entrée
conserve la voix avec laquelle l'outil parle, et la configuration continue.
Technique : Le paquet de journaux de l'utilisateur contenait quatre fois « GetVoices FAILED,
voice list empty: (0x80045039) », puis « SwitchToSetup ». Ce n'est pas un jeton isolé
qui était illisible (le cas de la 3.3) : sap.GetVoices() ou .Count lève lui-même
SPERR_NO_MORE_ITEMS, alors que sap.Voice.GetDescription() fonctionne. Nouveau :
RegistryVoiceTokens() dans sapi.ahk construit un SAPI.SpObjectToken par clé sous
Speech\Voices\Tokens (HKLM et HKCU) via SetId - uniquement en cas d'échec. Seules les
entrées ayant un nom et un CLSID sont listées, car une entrée défectueuse ne lève rien
ici, GetDescription() renvoie simplement "". AddVoiceNodes() dans menu.ahk remplace les
trois boucles identiques et ajoute l'entrée de secours quand la liste est vide. Vérifié
avec un banc d'essai qui charge le vrai sapi.ahk et fait lever exactement cette erreur à
GetVoices : mêmes voix que par la voie normale, toutes utilisables, deux entrées
volontairement défectueuses ignorées. La panne réelle n'a pas pu être reproduite ; pas
encore testé sur la machine de l'utilisateur.
Outil de connexion 3.4 : une voix défectueuse ne peut plus rendre l'outil muet
Simple : Une voix dont le programme a été supprimé mais dont l'entrée est restée dans
Windows figure toujours dans la liste des voix. La choisir était accepté par Windows
sans aucune erreur - et l'outil était ensuite muet, et même définitivement depuis le
menu principal, car le choix est enregistré. L'outil essaie désormais chaque voix de
façon inaudible avant de l'accepter. Si elle ne fonctionne pas, tout reste en l'état et
l'outil dit « Cette voix ne fonctionne pas, choisissez-en une autre ». Au démarrage non
plus, une voix enregistrée qui ne parle plus n'est pas appliquée ; la voix par défaut de
Windows reste en place.
Technique : Mesuré avec un enregistrement ayant un nom et un CLSID mais pas de moteur :
sap.Voice := token est accepté, seul Speak lève 0x80040154 (classe non enregistrée).
VoiceWorks() dans sapi.ahk parle donc sur un SpVoice séparé dans un SpMemoryStream, 16 à
31 ms, sans jamais toucher à la voix en cours. SetSapiVoiceByName et SetToolVoiceByName
renvoient maintenant le succès, VoiceSelectAction et ApplyToolVoice en tiennent compte.
SAPI2SR est exempté : le pont transmet le texte au lecteur d'écran quel que soit le flux
de sortie, l'essai serait donc audible. Une voix qui se charge mais ne produit que du
silence n'est pas détectée.
Installateur 5.2 : le paquet de journaux liste toutes les voix Windows
Simple : « Collecter les journaux » dans l'installateur ajoute désormais un fichier
voices.txt au paquet. Il nomme chaque voix enregistrée dans Windows et indique si le
programme correspondant existe encore. En cas de problème de voix dans l'outil de
connexion ou dans le jeu, on voit ainsi tout de suite quelle entrée est défectueuse.
Livré avec l'installateur 5.2.
Technique : LogCollector.WriteVoiceRegistrations lit Speech\Voices\Tokens,
Speech\Voices\TokenEnums et Speech_OneCore\Voices\Tokens dans les vues 64 bits et 32
bits du registre (l'outil de connexion est en 64 bits et ne voit pas les voix uniquement
32 bits) ainsi que dans HKCU, plus DefaultTokenId. Par entrée : nom, CLSID, la DLL
InprocServer32 avec vérification qu'elle est enregistrée et présente sur le disque,
VoicePath/LangDataPath et les attributs.
Installateur 5.2 : mesures contre les fausses alertes de Windows Defender
Simple : Quelques joueurs ont signalé que Windows Defender avait supprimé l'installateur
fraîchement téléchargé comme dangereux. L'installateur n'est pas dangereux ; notre
propre analyse Defender, avec des signatures à jour, n'y trouve rien. Deux choses qui
rendaient le fichier plus suspect que nécessaire sont corrigées : ses propriétés disent
maintenant ce qu'il est et de qui il vient (produit « Sku Installer », société « Sku »,
auteur JeanStiletto, lien vers le projet) au lieu d'être vides, et un fichier du pont
NVDA fourni ne porte plus une signature qui venait du PC du développeur et ne
signifiait rien sur une autre machine. Pour être honnête sur la limite : l'installateur
n'est toujours pas signé avec un certificat acheté. Chaque nouvelle version démarre
donc sans réputation de téléchargement, et Defender peut encore la retenir pendant ses
premiers jours. Si cela arrive : ouvrir Sécurité Windows, Historique de protection,
choisir l'entrée et autoriser ou restaurer le fichier - et nous envoyer le nom de la
détection, s'il vous plaît.
Technique : SkuInstaller.exe n'était pas et n'est pas signé Authenticode ; release.ps1 n'a
aucune étape de signature, seules les DLL du pont sont signées, sur la machine de
l'utilisateur, par sku-nvda-voice-sign.ps1. Un exe non signé n'a qu'une réputation cloud
par hash, et l'exe de la v43.5 avait un jour et neuf téléchargements au moment des
signalements. L'analyse locale (moteur 1.1.26080.3, signatures 1.459.278.0) est propre
pour l'exe, la charge utile du pont et l'AutoHotkey fourni : le verdict vient donc du
cloud/ML ou de l'analyse comportementale, pas d'une signature statique. Modifié :
Company, Product, AssemblyTitle, Description, Authors et Copyright dans
SkuInstaller.csproj (tous les champs disaient « SkuInstaller », copyright vide). Et
x86\sapi2sr_engine.dll dans payload\sapi2sr-payload.zip avait été recopiée depuis le
dossier Program Files du développeur et portait encore la signature jetable de cette
machine (partout ailleurs « signé par une racine non approuvée ») ; c'est maintenant la
DLL nue, 123320 -> 115712 octets. Le hash PE Authenticode exclut la signature, les deux
empreintes du script de signature correspondent toujours (5A76A572... x64, 1281E2B9...
x86) et la signature sur la machine de l'utilisateur est inchangée.
dev\rework-docs\nvda-voice-signing\strip_payload_signature.py fait ce retrait pour la
prochaine mise à jour de la charge utile. Non modifié, et toujours ce que voit une
analyse comportementale : exécution avec élévation, exécutables embarqués,
regsvr32 /s, un PowerShell caché qui ajoute un certificat de signature de code au
magasin racine de la machine, auto-remplacement.
-------------------------------------------------------------------------------------------------------
Changements dans Sku v43.5
Aperçu
Corrections
Base de données des quêtes : ouvrir « Tout » (ainsi que « Commence dans la zone » et « Quêtes à proximité ») figeait le jeu plusieurs secondes.
Chat : après ouverture de la ligne de chat avec « / », l'ancien canal était annoncé (par ex. « À Kai »), alors que seule la commande tapée ensuite décide du canal.
Voix : certaines lignes restaient muettes au hasard via le pont NVDA/SAPI (par ex. un objet de la liste d'équipement) alors que leurs voisines étaient lues.
Quête : « Le nettoyage selon Raene » (Bâtonnet de Dartol, Orneval) n'avait pas de cible ; elle mène maintenant au Coffre rouillé laissé par les Gelées pourrissantes.
Voix : les caractères tapés et les annonces annulées arrivaient en retard - des lettres d'une ligne de chat fermée depuis longtemps surgissaient plus tard dans un autre menu.
Outil de connexion 3.3 : une voix Windows endommagée sur le PC faisait quitter l'outil avec une fenêtre d'erreur (GetVoices, 0x80045039) juste après l'annonce de sa version.
Voix : en parcourant vite des entrées longues via le pont NVDA/SAPI, l'entrée précédente était lue jusqu'au bout avant celle choisie.
Base de données des quêtes : ouvrir les listes de quêtes figeait le jeu
Simple : Ouvrir la liste « Tout » sous Quête > Base de données des quêtes provoquait depuis la v43.2 un
blocage de plusieurs secondes. La liste s'ouvre de nouveau instantanément ; les entrées
d'une quête (acceptation, objectif, remise ...) ne sont construites qu'en entrant dans la
quête. Il en va de même pour « Commence dans la zone » et « Quêtes à proximité ».
Technique : « Tout » construisait d'emblée le sous-menu complet (CreateQuestSubmenu) pour
chacune des plusieurs milliers de quêtes de la base. Depuis la v43.2, la branche objectif
de ce sous-menu résout géométriquement le point de route le plus proche pour les quêtes à
lieu déclencheur (triggerEnd) via GetTriggerEndWps - un balayage complet du cache de
points de route d'un continent - et depuis la v43.3 pour chacune de ces quêtes, et non plus
seulement celles sans autre objectif. Une ouverture coûtait ainsi environ 170 balayages de
continent dans une seule image. Les entrées de quête sont désormais des nœuds dynamiques
avec leur propre BuildChildren, comme « Quêtes actuelles » l'a toujours été.
Chat : annonce de canal erronée après ouverture avec la touche slash
Simple : Si vous aviez chuchoté à quelqu'un en dernier et ouvriez la ligne de chat avec « / »,
vous entendiez « À Kai » - puis, juste après « /g », « Guilde ». La première annonce était
fausse : tant que la ligne commence par un slash, rien ne part vers ce canal, c'est la
commande qui décide. L'annonce reste désormais silencieuse tant qu'une commande est dans
la ligne, et dit le canal dès qu'il est fixé (« /g » devient « Guilde ») ou quand le slash
est effacé. Un passage volontaire à « Dire » (« /s ») est maintenant annoncé lui aussi,
sauf si Dire était de toute façon le canal par défaut. Au rappel d'une ligne envoyée
(flèche haut), le préfixe de canal est omis si la ligne est une commande.
Technique : L'en-tête de Blizzard affiche toujours l'attribut chatType, et à l'ouverture c'est
le canal « sticky » du dernier message envoyé (le chuchotement l'est aussi, tellTarget
compris). La touche slash appelle ChatFrameUtil.OpenChat("/") : le même en-tête plus un
« / » dans le champ, posé seulement au prochain OnUpdate (editBox.setText), si bien que
les appels UpdateHeader de l'ouverture tournent encore avec un champ vide. Un texte qui
commence par « / » ne part jamais vers le canal de l'en-tête : ce n'est que lorsque
ParseText résout la commande (« /g » à l'espace, « /w Nom » après le nom) que Blizzard
fixe chatType, SetText(reste) puis UpdateHeader. Sku vérifie désormais le texte effectif,
setText en attente compris (SkuChat:EditboxTextIsCommand), à chaque point d'annonce
(hooks UpdateHeader, focus, parole différée) ; un nouveau hook OnTextChanged couvre
l'effacement du slash, pour lequel Blizzard n'appelle aucun UpdateHeader.
Voix : certaines lignes restaient muettes au hasard
Simple : En parcourant rapidement une liste, une ligne isolée n'était parfois pas lue - par
ex. « Gants de combat naga » dans l'équipement, alors que ses voisines l'étaient - et elle
restait muette même en repassant dessus plusieurs fois. Cela concernait le pont NVDA/SAPI
et pouvait arriver partout dans Sku, surtout après un /reload. Chaque ligne est de nouveau
lue de façon fiable.
Technique : Le client WoW met en cache la voix rendue par texte et la rejoue lors d'une
répétition sans rappeler la voix - avec le pont, cette relecture est du silence. Sku
ajoute donc à chaque texte une suite d'espaces insécables comptée par texte (1..64) pour
que chaque répétition soit un texte nouveau. Mais le compteur ne vivait qu'en Lua et
repartait de zéro à chaque /reload, alors que le cache du client survit au reload (les
identifiants d'énoncés continuent sans rupture). Une ligne revue après le reload pouvait
ainsi retomber dans une plage déjà rendue et restait muette jusqu'à ce que le compteur en
sorte - six pressions d'affilée dans le journal, chacune avec un SpeakText + STARTED +
FINISHED propres. Les compteurs vivent maintenant dans SkuOptionsDB.global (bttsCacheBust)
et continuent au-delà du reload ; de plus l'espace par texte passe de 64 à 512 variantes
(des espaces insécables en tête, 1..8, portent la partie haute du compteur).
Quête : « Le nettoyage selon Raene » (Bâtonnet de Dartol) n'avait pas de cible
Simple : La quête 1027 « Le nettoyage selon Raene » en Orneval demande le dernier morceau du
Bâtonnet de Dartol, le pommeau de fer. Sku ne connaissait aucun lieu pour lui, « Cible de
quête » restait vide. La cible mène maintenant dans la zone des gelées au sud-est de la
Retraite de Raynewood : les Gelées pourrissantes y laissent un Coffre rouillé à leur mort,
et le pommeau s'y trouve.
Technique : L'objet 5519 (Iron Pommel) renvoie correctement à l'objet de jeu 19021 (Rusty
Chest) dans la base, mais cet enregistrement n'avait aucune coordonnée de spawn, ni dans
objects.lua ni dans objects_fixes.lua - la branche « item » de GetResultingWps ne trouvait
rien. objects_fixes.lua (TBC et Era) ajoute maintenant les 44 positions du coffre relevées
par Wowhead en Orneval (zone 331, environ 68-78 / 68-80) ; c'est la même surface que les
spawns de la Gelée pourrissante (3928) qui laisse le coffre.
Voix : les caractères tapés et les annonces annulées arrivaient en retard
Simple : Si vous tapiez un message un peu long puis fermiez la ligne de chat, vous
continuiez à entendre les lettres ensuite - une par une, alors que vous naviguiez depuis
longtemps dans un autre menu ; près d'une minute dans un enregistrement. Il en allait de
même pour une annonce annulée : après « Annulé » arrivait encore un reste de l'ancienne
ligne, par exemple une distance du menu des points de passage que vous veniez de fermer.
Tout ce qui reste en attente à la fermeture ou à l'annulation est désormais abandonné.
L'écho des touches reste complet - aucune lettre n'est perdue.
Techniquement : Le client accepte chaque SpeakText sans limite mais ne les joue qu'un à la
fois, et C_VoiceChat.StopSpeakingText n'annule que l'énoncé EN COURS - il n'existe pas
de vidage de file comme le PurgeBeforeSpeak de SAPI. Tout ce qui est déjà transmis est
donc irrécupérable. La voie de frappe transmettait un énoncé par touche, trois à quatre
par seconde, alors que le client en traite environ un : une ligne tapée de 104
caractères garait ainsi environ 80 énoncés dans le client, qui ressortaient ensuite un
par seconde. Pour la même raison CancelBttsOutput et EndScope restaient sans effet - ils
vident la file propre à Sku, où les caractères n'ont jamais été (85 des 93 appels à
EndScope d'un enregistrement n'ont rien abandonné). Les caractères attendent maintenant
dans Sku, un seul part vers le client à la fois, et le suivant seulement à son
PLAYBACK_FINISHED - le moment où le client accepte de nouveau un énoncé (chaque STARTED
de l'enregistrement coïncide avec un FINISHED). Rien n'est donc jamais transmis sans
être déjà en train de jouer, et une annulation atteint tout. Une courte fenêtre
d'annulation couvre le reste : après une annulation franche plus rien n'est transmis et
tout énoncé qui démarrerait malgré tout est stoppé aussitôt. Cela reprend la structure
de NVDA, qui garde lui aussi la file et ne donne à la voix qu'un énoncé à la fois.
Outil de connexion 3.3 : plantage au démarrage à cause d'une voix Windows endommagée
Simple : Après une installation neuve, l'outil annonçait encore « version 3.2 » puis
s'arrêtait avec une fenêtre d'erreur (« Error: (0x80045039) Specifically: GetVoices »).
En cause : une voix endommagée dans Windows - par exemple une voix désinstallée qui a
laissé des restes. L'outil ignore désormais une telle voix, la note dans log.txt et
démarre normalement ; elle manque seulement dans le menu « choisir la voix ».
Technique : 0x80045039 est SPERR_NO_MORE_ITEMS : SAPI annonce N voix, mais l'une d'elles
est illisible (entrée cassée sous HKLM\SOFTWARE\Microsoft\Speech\Voices\Tokens).
L'outil plantait en construisant le menu des voix, dans la boucle for sur
sap.GetVoices(). GetVoices() et SetSapiVoiceByName() passent maintenant par la
nouvelle fonction ReadableVoiceTokens(), qui lit chaque voix par Item(i) dans son
propre try ; une voix illisible est ignorée et journalisée, et si la liste elle-même
échoue, elle reste vide. SapiInit() lit la voix par défaut avec la même protection.
Pas encore testé sur un PC avec une voix cassée.
Voix : un défilement rapide via le pont NVDA lisait l'entrée longue précédente jusqu'au bout
Simple : Avec la voix du pont NVDA/SAPI, parcourir vite une liste aux entrées longues (liste
des points de passage, textes de quête, historique du chat) lisait d'abord l'entrée
précédente en entier, puis seulement celle sous le curseur. L'ancienne entrée est
maintenant coupée après un bref instant et celle choisie est lue, comme avec une voix
Windows. Les textes longs en plusieurs parties restent lus en entier, et l'écho de
frappe ne change pas. La correction nécessite le nouveau pont NVDA installé par
l'installateur Sku 5.1 : mettez à jour via l'installateur (pas seulement le zip de
l'addon) puis redémarrez complètement WoW.
Technique : StopSpeakingText du jeu n'arrête que la lecture du client. Sur le pont, le texte
est déjà dans la file de NVDA, et SAPI ne transmet jamais « purge before speak » à un
moteur : rien sur le chemin ne pouvait interrompre NVDA. Seul l'énoncé lui-même,
balisage compris, atteint le moteur - l'intention voyage donc à l'intérieur, comme avec
speak(text, interrupt) de Prism : chaque arrêt pose un drapeau, et la ligne suivante
porte . Le moteur livré par l'installateur (SAPI2SR avec le
patch C de Sku) appelle nvdaController_cancelSpeech en voyant ce signet, puis transmet
le texte. Les lignes transmises sans arrêt préalable ne portent pas de marque et
restent en file. Les vraies voix SAPI ignorent le signet ; un moteur plus ancien le
saute.
-------------------------------------------------------------------------------------------------------
Changements dans Sku v43.4
Aperçu
Nouveautés
Pawn : Addons > Pawn regroupe les réglages d'infobulle de Pawn, et les infobulles Sku annoncent l'amélioration en pour cent, la différence par attribut et les attributs manquants. Par Yennesta.
Nouvelle touche « ouvrir le compteur de dégâts » : accès direct aux rapports de Details ; les raccourcis ont désormais leur propre groupe « Extensions ».
Changements
Chat : à l'envoi ou à l'annulation, un écho clavier encore en cours est arrêté de façon fiable, sans jamais couper la lecture du message envoyé.
Les annonces d'un menu ou d'une fenêtre s'arrêtent avec la fenêtre : ce qui attend encore à la fermeture est abandonné, ce qui est en cours est stoppé.
Corrections
Mac : les touches fléchées, de fonction et de page arrivaient deux fois - les emplacements vides de sacs et de barres étaient sautés, et le lecteur d'infobulle repartait du début à chaque Maj+Flèche bas.
Moniteur de vie du raid : un rôle attribué à la main tombait sur le mauvais joueur, et sans détection de rôle le moniteur restait muet.
Marchand : l'onglet Rachat lisait et achetait l'objet portant le même numéro dans le stock normal.
La comparaison avec l'objet équipé manquait parfois dans les infobulles - l'infobulle de lecture avait perdu son propriétaire.
Lecteur d'infobulle : en fin de texte, par exemple dans la liste d'amis, Maj+Flèche bas alternait entre silence et dernière ligne.
Les annonces demandées par touche (vie de la cible, distance, cible ...) restaient muettes à une seconde pression dans la même seconde.
Mac : les touches fléchées et de fonction arrivaient deux fois
Simple : Sur Mac, le défilement rapide sautait les emplacements vides de sacs et de barres
d'action, et le lecteur d'infobulle recommençait au début à chaque Maj+Flèche bas. Les
deux sont corrigés ; Windows n'a jamais été touché.
Technique : Pour les touches sans caractère - flèches, touches F, page haut et bas, début,
fin, insertion, suppression - le client Mac transmet à OnChar les caractères de
substitution du système, pris dans la zone Unicode à usage privé (U+F700 et suivants) ;
Windows ne déclenche pas OnChar pour ces touches. Chaque pression de flèche atteignait
donc le menu deux fois : comme touche liée et comme caractère fantôme transmis sans
contrôle au gestionnaire de touches. Le compteur de double appui y comptait deux
pressions par pression (chaque entrée « Vide » sautée), et la règle « toute autre
touche ferme le lecteur » fermait le lecteur d'infobulle après chaque Maj+Flèche bas.
Le menu ne transmet plus depuis OnChar que les caractères connus de la recherche par
lettres (liste positive), les deux hooks d'écho clavier rejettent toute la zone à usage
privé U+E000 à U+F8FF, et un double appui exige désormais deux fois la même touche.
Les caractères rejetés vont dans l'anneau de débogage avec leurs octets, pour qu'un
testeur puisse prouver le fantôme sur son client. Diagnostiqué et paré en premier par
Yennesta (PR 19).
Moniteur de vie du raid : les rôles sur le bon joueur
Simple : Attribuer un rôle à la main à un membre du raid touchait souvent un autre joueur dès
que l'ordre des groupes différait de l'ordre d'arrivée. Et sans détection automatique
de rôle, le moniteur restait complètement muet pour ce joueur.
Technique : Le menu d'attribution numérote les membres par leur numéro audible, trié par
groupe, et enregistrait le rôle sous ce numéro ; l'évaluation de la vie le lisait via
l'index réel raidN. Le menu garde sa numérotation et retient l'index réel par entrée,
sous lequel il enregistre et lit. Sans rôle (SkuAuras désactivé, ou aucun détecté),
l'identifiant restait nil et UNIT_HEALTH indexait la table de filtres avec - abandon de
toute l'évaluation. La résolution du rôle est désormais une fonction : attribution,
puis SkuAuras, sinon la catégorie « Sans rôle ». Par Yennesta.
Marchand : le rachat rachète bien ce qui a été vendu
Simple : Dans l'onglet Rachat, Sku nommait le mauvais objet et, à la confirmation, achetait
l'offre du marchand portant le même numéro au lieu de la pièce vendue.
Technique : L'onglet Rachat réutilise les boutons et numéros du stock normal. Sku les lisait
avec SetMerchantItem et achetait avec BuyMerchantItem, tous deux relatifs au stock.
Onglet Rachat actif, la lecture passe désormais par SetBuybackItem et l'achat par
BuybackItem ; un rachat est une seule pile, il n'y a donc pas de sous-menu de quantité.
Par Yennesta.
Pawn : amélioration et différences de caractéristiques dans l'infobulle
Simple : Avec Pawn installé, l'infobulle Sku d'un objet annonce juste sous le nom
l'amélioration en pour cent et l'échelle, ajoute à chaque ligne d'attribut la
différence avec l'objet équipé (dite « plus » ou « moins », dégâts par seconde avec une
décimale) et liste les attributs que l'objet équipé possède et que le nouveau n'a pas.
Addons > Pawn regroupe l'interrupteur « Pawn dans les infobulles Sku » et les réglages
d'infobulle propres à Pawn ; sans Pawn, il n'y a qu'un rappel de son absence. Par
Yennesta.
Technique : Nouveau fichier SkuCore/pawnIntegration.lua ; chaque appel à Pawn est protégé et
le passage sur l'infobulle tourne dans pcall, sans Pawn rien n'est ajouté. Par rapport à
la contribution : l'interrupteur est une valeur par défaut du schéma de réglages SkuCore
(activé), la correspondance de ligne ignore la casse (sur les clients anglais la
statistique s'appelle « Damage Per Second », la ligne d'infobulle « damage per second » -
la différence de DPS y disparaissait en silence), et un passage en échec écrit son
erreur dans l'anneau de débogage.
Touche « ouvrir le compteur de dégâts » et groupe de touches « Extensions »
Simple : Une nouvelle touche, non attribuée par défaut, mène directement à Addons > Damage
Meter > Rapports. Les raccourcis ont désormais un groupe « Extensions » avec les touches
d'AtlasLoot et du compteur de dégâts.
Technique : Liée et évaluée comme la touche du navigateur de donjons ; le chemin de menu passe
par des identifiants de noeuds (Addons, DamageMeter, Reports) et fonctionne donc dans
chaque langue. Les entrées de rapport fournissent leur texte à la lecture (une fonction
au lieu d'une table, comme les entrées de l'hôtel des ventes) : données Details fraîches,
aucun coût pour les combats non lus, et cela marche aussi quand le curseur arrive sur une
entrée par touche sans passer par OnEnter. Touche et identifiants par Yennesta.
La comparaison avec l'objet équipé manquait parfois
Simple : Avec Maj+Flèche bas sur un objet, la section « objet actuellement équipé » manquait
parfois, apparemment au hasard. Elle est désormais toujours là.
Technique : Sku lit les infobulles via un GameTooltip partagé et invisible. Un GameTooltip ne
remplit ses lignes que tant qu'il a un propriétaire, et le perd tout seul : un appel
sans contenu (emplacement de barre, de sac ou d'équipement vide, objet pas encore
chargé) le masque et efface le propriétaire. Dès lors chaque lecture ne renvoyait rien
en silence, jusqu'à ce qu'un autre chemin appelle SetOwner par hasard. Trois endroits
l'avaient déjà corrigé pour eux-mêmes, ce qui donnait au défaut son air aléatoire. Les
18 points de lecture rétablissent désormais le propriétaire avant chaque lecture via une
fonction commune.
Lecteur d'infobulle : la fin d'un texte
Simple : Dans la liste d'amis (et partout ailleurs), le lecteur ne restait pas sur la dernière
ligne avec Maj+Flèche bas mais alternait entre silence et dernière ligne - comme une
boucle sans fin. Il relit désormais la dernière ligne à chaque pression.
Technique : En fin de texte, le lecteur retransmet la même ligne à la couche vocale. Son
garde-fou anti-doublons de la v43.2 rejette une ligne identique dans la même seconde
sauf si elle est marquée comme déclenchée par touche - et le lecteur ne marquait jamais.
Chaque ligne du lecteur, liens compris, porte désormais la marque.
Les annonces demandées par touche ne restent plus muettes
Simple : Vie de la cible, distance, annonce de cible, infos de jet, se tourner vers la balise,
touches de points de passage et autres : une seconde pression dans la même seconde avec
la même réponse restait muette. La réponse vient désormais à chaque pression.
Technique : Le même garde-fou ; seul le gestionnaire de touches du menu marquait jusqu'ici.
Plutôt que de marquer chaque annonce une à une, les trois répartiteurs de touches (cadre
principal, cadre de contrôle SkuCore, SkuNav) retiennent la frame de leur appel, et la
couche vocale traite toute transmission dans les 0,1 seconde qui suivent comme
déclenchée par touche. La ré-annonce d'une reconstruction de menu vient d'un
minuteur, dans des frames ultérieures, hors de cette fenêtre ; le gestionnaire du menu
continue de marquer explicitement.
Portées vocales : les annonces s'arrêtent avec leur fenêtre
Simple : Deux cas avec des voix SAPI lentes : après envoi ou annulation dans le chat, la voix
finissait de lire une ligne rappelée par Flèche haut, et après trois apprentissages
chez le maître et Échap, le maître était encore annoncé trois fois. Les deux sont
réglés : l'écho du chat est coupé à la fermeture - jamais la lecture du message envoyé -
et ce qu'un menu ou une fenêtre avait encore à dire à la fermeture est abandonné ou
stoppé. Les annonces de combat, de chat ou de navigation ne sont pas concernées.
Technique : Une pression de touche seule n'interrompt toujours rien (règle de la v43.2), et
sur la passerelle du lecteur d'écran la fin d'un énoncé est indéterminable, car FINISHED
arrive dans la frame de transmission - la comptabilité « quelque chose parle » y est
toujours vide, raison pour laquelle même l'arrêt à la fermeture du menu était supprimé
comme inutile. Ce qui est connu exactement, c'est ce qui a été transmis en dernier et
pour qui. Une ligne peut désormais porter une portée (« menu » pour les annonces de
menu, « echo » pour la voie de frappe), et SkuVoice:EndScope abandonne les lignes en
attente de cette portée à sa fin et n'arrête la voix que si l'énoncé en cours lui
appartient. C'est appelé à la fermeture du menu et dans le hook Hide de chaque fenêtre
surveillée, donc aussi sur l'Échap du jeu. L'arrêt doux du champ de chat interroge
désormais SkuVoice:IsEchoInFlight au lieu de la règle d'une seconde : l'écho est coupé
quelle que soit sa durée ; si autre chose a été transmis depuis - avant tout la réponse
du serveur au message envoyé - rien n'est arrêté. Yennesta avait proposé l'annulation
inconditionnelle dans la PR 19 ; ceci en est la forme ciblée.
-------------------------------------------------------------------------------------------------------
Changements dans Sku v43.3
Aperçu
Nouveautés
Le programme d'installation existe désormais aussi pour Mac - installation, mise à jour et auto-mise à jour comme sous Windows. Merci à Yennesta.
Programme d'installation 5.0 : sept addons compagnons éprouvés comme Questie et Deadly Boss Mods peuvent être installés en plus et sont tenus à jour - Windows et Mac, Anniversary et Classic Era.
Mac : le programme d'installation et l'outil de connexion parlent allemand, anglais et français ; l'outil de connexion reconnaît aussi les clients français.
Changements
Se tourner vers l'unité et Se tourner vers la cible tournent désormais comme la rotation vers la balise : le chemin le plus court, un quart de seconde au maximum.
Corrections
Synthèse vocale : certaines annonces restaient muettes alors que le son était joué - surtout les lignes courtes que l’on entend en boucle.
Touche Interagir : après un kill, elle attrape parfois une créature au loin et vous y fait courir au lieu de dépouiller - Sku vous prévient maintenant par un son.
Mac : la synthèse vocale lisait à chaque ligne un balisage technique de contrôle.
Maître des écuries : le menu affichait et échangeait les mauvais emplacements de familier, et l'annonce d'échange signalait un échec trop tôt.
Chat : un onglet Journal de combat restait complètement muet.
Le Ciblage au cadran était complètement mort sur le client Anniversary - il fonctionne de nouveau, testé en raid à 25.
Français : quatre textes des réglages du chat parlaient encore anglais.
Français : quatre textes du chat parlaient encore anglais
Simple : Sur un client français, le réglage des flèches dans la saisie du chat, l'annonce
du canal avec son explication et le message pour une commande inconnue s'entendaient
encore en anglais. Ils sont maintenant traduits.
Technique : Ces quatre textes de la v43.1 manquaient dans le magasin de traduction à
partir duquel le fichier de langue français est généré. En corrigeant cela, il est
apparu que 29 textes écrits à la main dans le fichier généré pour la v43.2
n'existaient qu'à cet endroit - la prochaine génération les aurait silencieusement
ramenés à l'anglais. Tous sont désormais repris dans le magasin, et l'outil de
génération n'écrit plus le fichier que sur commande explicite, au lieu de le faire
pour tout argument inconnu.
Synthèse vocale : les annonces répétées ne restent plus muettes
Simple : En naviguant dans les menus et les infobulles, on n’obtenait parfois que le son,
sans l’annonce elle-même. Cela touchait surtout les lignes courtes que l’on entend en
boucle toute la soirée - « menu fermé », « accepter », « buffs », ou un simple caractère
tapé.
Technique : Le client conserve en mémoire la parole déjà synthétisée pour un texte donné et,
en cas de répétition, la rejoue depuis ce cache sans rappeler la voix. Via une
passerelle vers un lecteur d’écran, cette relecture est muette. Sku ajoute donc à chaque
ligne un marqueur inaudible afin que chaque appel soit régénéré - jusqu’ici à partir
d’un compteur qui recommençait toutes les 64 annonces. Une ligne revenant exactement 64
annonces plus tard était donc identique caractère pour caractère et retombait dans le
cache. Le compteur est désormais tenu par texte et non plus en commun ; une ligne vue
pour la première fois reçoit toujours sa valeur de départ du compteur commun, afin que
le vidage de la table (à 512 lignes distinctes) ne renvoie aucune ligne sur sa propre
première valeur. Rejoué sur un enregistrement de 1307 annonces : 27 répétitions
identiques auparavant, 80 avec un compteur par texte privé de cette valeur de départ -
donc pire - et 0 dans la version livrée.
Mac : la synthèse vocale ne lit plus le balisage de contrôle
Simple : Sur Mac, la voix lisait à voix haute, avec les lignes, des caractères de
contrôle destinés uniquement à la technique. Ils ont disparu là-bas.
Technique : Pour la poignée de main avec le lecteur d'écran, Sku ajoute un marqueur
à chaque ligne ; le client Mac ne filtre pas ce balisage de la synthèse et
le lisait tel quel. Sur Mac, le marqueur est désormais omis ; les caractères
inaudibles qui déjouent le cache de répétition restent, si bien que chaque répétition
y est elle aussi régénérée. Idée de Yennesta.
Se tourner vers l'unité : plus rapide et plus précis
Simple : « Se tourner vers l'unité » et « Se tourner vers la cible » tournent désormais
exactement comme la rotation vers la balise : par le chemin le plus court, en un
quart de seconde au maximum, au lieu de chercher jusqu'à une seconde et demie en
tournant vers la droite.
Technique : Le coeur de rotation construit et longuement testé pour la v43.2 (alignement
de direction, plafond de vitesse, anticipation en mouvement, remise à plat avec le
verrou d'inclinaison) a été séparé de la résolution des points de passage ; la
rotation vers une unité l'appelle donc dès que le client fournit des coordonnées pour
l'unité. L'ancienne recherche par barres de nom reste en secours pour les instances,
où il n'y a pas de coordonnées. La résolution de la cible vers une unité suit une
idée de Yennesta.
Maître des écuries : l'échange de familier retrouve les bons emplacements
Simple : Dans le menu du maître des écuries du chasseur, les emplacements étaient
décalés - le mauvais familier était affiché et échangé. De plus, sur une connexion
lente, l'échange annonçait à tort que le familier n'avait pas pu être changé alors
qu'il avait réussi. Les deux sont corrigés, et l'annonce donne désormais le nom du
nouveau familier.
Technique : La fenêtre d'écurie de Blizzard utilise les indices d'action 1 à 3 (familier
actif plus deux emplacements) ; Sku interrogeait un cran trop bas. Correctif de
Yennesta. L'annonce de réussite ne se décide en outre plus sur le premier événement
d'écurie (qui se déclenche déjà à la prise en main), mais attend que le nom du
familier actif corresponde ou que deux secondes soient passées - et elle est
maintenant traduite dans les trois langues.
Nouveau : le programme d'installation existe aussi pour Mac
Simple : Sku s'installe et se tient à jour sur macOS aussi confortablement que sous
Windows : un programme d'installation avec sortie vocale, auto-mise à jour et les
mêmes composants. La page de téléchargement propose un lien par plateforme. Contribué
par Yennesta - merci !
Technique : La branche Mac est accrochée au même flux de publication que Windows : la
détection de version suit la redirection releases/latest de GitHub au lieu de lire
une page web, et les fichiers avec leurs sommes sha256 sont vérifiés localement à la
publication puis attachés à la même publication.
Touche Interagir : un son d'avertissement quand elle attrape la mauvaise cible
Simple : Juste après la mort d'une créature, la touche Interagir (G par défaut) prend
parfois pour cible une tout autre créature attaquable - même lointaine - au lieu du
cadavre devant vous, et vous y fait courir automatiquement au lieu de dépouiller. Sku
joue maintenant un son d'avertissement à cet instant précis, pour que vous entendiez
aussitôt la mauvaise prise et puissiez annuler la course automatique à la main.
Technique : La touche appelle InteractUnit("anyinteract") de Blizzard avec un ciblage
relâché ; quand le cadavre tout juste dépouillé disparaît sous l'appui, le client
attrape de façon relâchée l'unité attaquable la plus proche à portée de Tab, en fait
votre cible dure et y court. La touche elle-même ne peut pas être remplacée (InteractUnit
est protégée), et la cible ne peut pas non plus être effacée depuis un addon (ClearTarget
est protégée aussi, en combat comme hors combat). Reste la détection : juste après qu'un
cadavre quitte le réticule, une cible vivante, attaquable et PAS en combat avec vous -
alors le son. Une cible choisie volontairement au clavier (Alt+H / Ctrl+H) ne le
déclenche pas.
Programme d'installation 5.0 : installer des addons compagnons éprouvés
Simple : Sur la page des composants, sept addons éprouvés peuvent désormais être cochés -
Questie, Deadly Boss Mods, AtlasLoot, Details, GTFO, BugSack avec BugGrabber, et
Pawn. Ce qui est coché, le programme d'installation l'installe avec Sku et le tient à
jour à chaque exécution - sous Windows et sur Mac, pour Anniversary et Classic Era,
chaque client recevant la version qui lui correspond. Tout est activé par défaut sauf
Pawn.
Technique : Les deux plateformes lisent le même catalogue. Les addons publiés sur GitHub
viennent directement de là (résolus via la redirection releases/latest, sans clé
d'API ni limite de débit) ; le reste est épinglé sur des fichiers CurseForge vérifiés
par sommes sha256. Deadly Boss Mods est une entrée composée de quatre paquets, et
BugSack et BugGrabber partagent délibérément une seule case. Chaque client reçoit la
version qui lui correspond - Pawn pour Classic Era vient par exemple du paquet
Classic.
Mac : programme d'installation et outil de connexion en trois langues
Simple : Les deux parlent désormais allemand, anglais et français. La langue se change
dans le menu correspondant et suit sinon la langue du système. L'outil de connexion
reconnaît en outre les clients français et lit les lignes de niveau mot pour mot - un
client allemand redit donc « Stufe 36 » au lieu de « Level 36 ».
Technique : L'application, le script d'installation et l'outil de connexion partagent un
même choix de langue (réglage enregistré avant langue du système avant anglais) ; les
valeurs enregistrées comme les noms des packs vocaux restent allemandes en interne
pour que les préférences existantes continuent de fonctionner. La détection d'écran
de l'outil de connexion est indépendante de la langue de l'interface.
Chat : le journal de combat reçoit de nouveau ses événements
Simple : Un onglet de chat « Journal de combat » restait complètement muet. Il se remplit
de nouveau. Merci au testeur de la communauté pour le signalement accompagné de la
solution.
Technique : L'onglet s'était abonné à l'événement COMBAT_LOG_EVENT, qui ne se déclenche
pas sur ce client comme le code l'attend ; il s'abonne désormais à
COMBAT_LOG_EVENT_UNFILTERED - le même événement que les autres modules de Sku
utilisent depuis longtemps.
Le Ciblage au cadran fonctionne de nouveau
Simple : Le ciblage par le pavé numérique (entrée « Ciblage au cadran » dans le menu Sku)
était complètement mort sur le client Anniversary - aucun appui n'arrivait. Il
fonctionne de nouveau, testé en raid à 25. En plus : dans un groupe normal (hors
raid), il n'avait jamais trouvé personne, les membres 30 à 40 n'étaient pas
composables, et un numéro à moitié saisi pouvait corrompre la saisie suivante - tout
est corrigé.
Technique : Le client moderne ne délivre les clics clavier que si le bouton les a
enregistrés via RegisterForClicks - cela manquait, ce qui rendait toute la fonction
muette. L'action se déclenche en outre exactement une fois par appui, quel que soit
le réglage de clic de l'utilisateur. La disposition du groupe vient de party1 à
party4 au lieu de la seule requête de raid, le seuil du premier chiffre laisse passer
les sous-groupes 6 à 8, et la grille de touches remplie est écrite dans le journal de
débogage à chaque changement. Note : les numéros correspondent à la section raid de
la page d'aperçu ; la section groupe y compte toujours vous-même comme 1 et décale
les autres - c'est voulu et non un défaut du ciblage.
-------------------------------------------------------------------------------------------------------
Changements dans Sku v43.2
Aperçu
Nouveautés
Les quêtes peuvent être suivies ou retirées du suivi depuis le menu de quête - nouvelle entrée « Suivre la quête » en bas de chaque menu de quête, désactivable.
Nouvelle liste « Quêtes à proximité » dans le menu de quête : tout le journal de quêtes en une seule liste, triée selon la distance de ce qui compte pour chaque quête - avec la distance et le point cardinal. Désactivée par défaut.
Nouveau raccourci « Cibler la créature de quête la plus proche » (Alt+H) : cible une créature liée à un objectif de quête en cours - y compris celle qui se contente de faire tomber un objet requis.
Le raccourci « Ennemi suivant en combat » (désormais sur Ctrl+H par défaut) trouve bien plus souvent quelque chose : il ne dépend plus du cône de vue.
Nouveau raccourci « Annoncer l'infobulle complète de la cible » (Ctrl+Maj+V) : lit à voix haute l'infobulle complète de la cible actuelle, y compris ce que révèle la compétence de chasseur Connaissance des bêtes.
Menu des sacs : nouvelle entrée « toute la banque » juste après « tous les sacs » tant que la banque est ouverte - tout le contenu de la banque en une seule liste à plat, exactement comme « tous les sacs » pour vos sacs. Ctrl+Entrée y déplace un objet vers vos sacs, comme il le faisait déjà dans chaque sac de banque.
Chat : la ligne de saisie parle désormais comme tout autre champ de saisie de Sku - chaque caractère tapé, le caractère ou le mot sous le curseur, et avec la flèche haut les derniers messages envoyés, pour les renvoyer ou les modifier.
Chat : le canal cible est annoncé dès qu'il n'est pas « Dire » - à l'ouverture et à chaque changement ; désactivable dans les réglages du chat, activé par défaut.
Chat : une commande slash inconnue est maintenant annoncée au lieu d'être ignorée en silence.
Nage et vol : Sku vous maintient à l'horizontale. Le nouveau verrouillage de l'inclinaison met fin à la plongée insidieuse lors des virages automatiques, et après une approche automatique vers un PNJ ou un appui sur monter/descendre, vous êtes activement redressé. Activé par défaut ; désactivable sous Paramètres -> Général.
Nouveau raccourci « verrouillage de l'inclinaison en nage et en vol » (Ctrl+Maj+N) : verrouille et libère l'inclinaison manuellement - l'éteindre puis le rallumer vous remet à l'horizontale à tout moment.
Expérimental : itinéraires de récolte pour les minerais, les herbes, les nuages de gaz et les coffres - Sku vous guide de gisement en gisement à travers la zone, le plus proche d'abord. Sous Réglages -> Scan -> analyse des ressources -> Itinéraires de récolte. Nouveau et à peine testé en jeu ; vos retours sont expressément souhaités.
Nouveau menu « Barres de nom » sous Réglages -> Aides visuelles : taille des barres, mise en évidence de la cible, couleurs de classe et atténuation des autres barres - des réglages que le jeu sait faire mais n'offre nulle part dans sa propre fenêtre d'options.
La fenêtre de lecture des textes de quête, infobulles, historique de discussion et wiki devient configurable : taille de police, police, couleurs, opacité et contour sous Réglages -> Aides visuelles -> Fenêtre de texte.
Changements
Se tourner vers la balise (T) : la rotation est désormais nettement plus précise. Avant, elle tournait si vite que le simple arrêt à la trame près pouvait coûter 20 degrés et plus de dépassement selon la fréquence d'images ; la vitesse s'adapte maintenant à l'ampleur de la rotation, et aucune rotation ne dure plus d'un quart de seconde.
Se tourner vers la balise : en mouvement, la rotation anticipe - elle vise l'endroit où se trouvera le point de passage à la fin de la rotation. Cela met fin aux tours en rond autour des points proches, surtout connus en monture et en vol.
Se tourner vers la balise : un appui pendant une rotation en cours est ignoré au lieu d'interrompre la rotation - appuyer plusieurs fois continue d'affiner comme avant.
Navigation : sous « Point de passage direct », « Carte actuelle par distance » vient désormais en premier et « Récent » en second.
Base de données des quêtes : « Commence dans la zone » affiche directement la liste triée par distance - le niveau intermédiaire « Par distance » a disparu.
Base de données des quêtes : « Commence dans la zone » indique désormais aussi le point cardinal de chaque quête, et non plus seulement la distance.
Base de données des quêtes : mise au niveau des données actuelles de Questie - 33 quêtes sans cible en ont désormais une, cinq quêtes manquantes ajoutées, plus environ 2000 petites corrections de noms, niveaux, donneurs de quête et prérequis.
Sacs : après un clic droit sur un objet, le curseur reste sur cet objet au lieu de sauter en tête de liste.
Sacs : les actions avec temps d'incantation (enchantement, appât de pêche, huile d'arme, kit d'armure, désenchantement) annoncent à nouveau l'objet une fois l'incantation terminée et y laissent le curseur.
Sacs : une boîte de dialogue de confirmation comme « Remplacer l'enchantement existant ? » ne coûte plus la position - après « Oui » comme après « Non », on revient sur l'objet.
Chat : « Ajouter le lien au chat » insère l'objet à la position du curseur au lieu de remplacer la ligne, et laisse le curseur devant - un canal comme « /p » tient donc encore avant.
Chat : un lien d'objet compte pour un seul caractère à la lecture - les flèches gauche et droite franchissent tout le lien et en disent le nom.
Chat : « Ajouter le lien au chat » ne ferme plus les sacs.
Chat : les flèches déplacent le curseur de texte dans la saisie du chat ; désactivable dans les paramètres du chat.
Auras : un enchantement d'arme (appât de pêche, poison, huile) s'adresse désormais comme un sort - l'événement « Enchantement d'arme expiré » avec « nom du sort ».
Auras : nouvelle condition et nouvelle sortie « main de l'enchantement d'arme » (main droite ou main gauche).
Menu : la fermeture d'une fenêtre ne reconstruit plus les données du menu qu'une seule fois au lieu de six.
Menu : le filtre de saisie ignore les accents - « general » trouve maintenant « Fournitures générales », et « ingenieur » trouve « Ingénieur ». Sur un clavier AZERTY, la touche ù alimente désormais aussi le filtre.
Français : les commentaires de points de passage existent maintenant en français - 207 textes sur 546 points de passage, traduits depuis l'original allemand.
Programme d'installation : un WoW installé à un endroit inhabituel ne doit être indiqué qu'une seule fois - le programme d'installation retient désormais où se trouve chaque client et le retrouve tout seul à chaque mise à jour ultérieure.
La fenêtre de lecture démarre désormais dans une police sans empattement en 18 points au lieu de Playfair Display en 12 - l'ancien réglage par défaut était la surface de texte la plus petite et la plus fine de tout l'addon.
La barre de lecture se règle elle aussi en police et en couleurs, avec ses propres valeurs distinctes de celles de la fenêtre de texte.
Hôtel des ventes : « Niveau décroissant » trie désormais toute la liste des résultats et non plus sa seule première page ; à niveau égal, c'est le prix à l'unité qui décide.
Hôtel des ventes : « niveau » désigne partout le niveau requis de l'objet - et n'est lu que là où il distingue réellement les entrées.
Corrections
Menu : pendant la marche, un menu ouvert était comme mort - toutes les touches sauf Échap étaient avalées jusqu'à l'arrêt complet.
Échap ne fermait pas le menu lorsqu'on se tenait juste sur un objet de quête - la fenêtre de quête se rouvrait sans fin.
Menu : Entrée sur une entrée activé/désactivé dans une liste filtrée annonçait la première entrée de la liste au lieu de l'état basculé.
Menu : une ligne redemandée par une touche est relue au lieu d'être avalée.
Menu : le filtre de saisie interprétait le texte tapé comme un motif de recherche - une saisie comme « 50% » ou « (long » ne trouvait rien ou échouait.
Écho clavier : les caractères tapés sont annoncés immédiatement ; plus de lettres avalées ni en retard.
Synthèse vocale : les annonces devenaient régulièrement muettes pendant un moment lors de la navigation - surtout sur un résultat unique après filtrage.
Sortie vocale : les liens d'objet étaient lus comme une suite de codes de couleur au lieu de leur nom - partout dans Sku, pas seulement dans le chat.
Se tourner vers la balise : après des rotations déclenchées coup sur coup, la vitesse de rotation des touches de caméra pouvait rester quadruplée en permanence.
La suppression d'un point de passage personnel retirait le mauvais identifiant interne, et « /skucheck wp » signalait quatre erreurs sur les points de passage rapides qui n'en étaient pas.
Menu de quête : pour les quêtes à objectifs mixtes (« tuer X et ramasser Y »), l'entrée « Cible de quête » ne montrait que le premier type d'objectif - ennemis, sources d'objets et point d'exploration figurent désormais côte à côte.
Menu de quête : pour 174 quêtes dont l'objectif est un LIEU, le lieu figurait bien dans les données, mais « Objectif de quête » ne répondait que « Vide » - la recherche portait sur un point de passage dont personne n'attribue jamais le nom. Le point de passage de route le plus proche est désormais calculé à partir des coordonnées.
Pour 77 AUTRES quêtes, les données d'objectif manquaient en revanche totalement - objets, créatures et 26 coordonnées de lieu y ont été ajoutés, une partie provenant de sources web : vos retours sur leur exactitude sont les bienvenus.
Menu de quête : environ 300 quêtes dont le donneur, la cible ou la remise se trouve dans un donjon affichaient des entrées vides - elles mènent désormais à l'entrée du donjon.
Treize quêtes de dialogue (dont les anciennes quêtes de sorts de prêtre) n'avaient aucun point de remise dans les données - l'entrée Livraison mène désormais au PNJ que nomme le texte de la quête.
Base de données des quêtes : juste après un /reload, des quêtes d'événements mondiaux inactifs (par exemple les quêtes du Solstice d'été) pouvaient apparaître dans « Commence dans la zone ».
Base de données des quêtes : la liste « Commence dans la zone, Par distance » s'interrompait après la première quête.
Sacs : le filtre de saisie restait bloqué après l'utilisation d'un objet et ne pouvait plus être levé qu'en fermant les sacs.
Après une action avec temps d'incantation, un mauvais objet était annoncé juste après le bon.
La liste des sacs tenue pour le combat pouvait se périmer lorsqu'on fermait une fenêtre de quête alors que les sacs étaient ouverts.
Menu des sacs : avec un addon de remplacement des sacs comme Bagnon, qui masque la fenêtre de banque de Blizzard, tous les compartiments de banque disparaissaient du menu.
Auras : le buff Rassasié figurait deux fois dans le sélecteur de sorts - les deux entrées sonnaient pareil et une seule pouvait se déclencher.
Auras : les entrées qui n'existent que comme enchantement d'arme (par exemple « Compétence Pêche +75 ») sont désormais signalées comme telles dans les sélecteurs.
Auras : « enchantement d'arme main droite contient X » avec « Enchantement d'arme expiré » ne pouvait jamais se déclencher - /skucheck auras signale désormais cette combinaison.
Auras : la fin d'un enchantement d'arme est détectée à la frame près au lieu d'un quart de seconde plus tard.
Barres d'action : Maj+Bas ne lisait plus l'infobulle du sort dans le menu d'affectation.
Navigateur de donjons : accepter une invitation de groupe ne vous dépose plus tout seul dans le navigateur de donjons.
Français : les commentaires de points de passage étaient totalement muets depuis la v42.11 - y compris les avertissements comme « attention, ravin » ou « attendre le zeppelin ici ».
Français : 22 des 94 noms de ressources du scanner de minicarte étaient erronés, ces gisements n'étaient donc jamais annoncés.
Français : dans la Caverne des Brumes, la liste des points de passage restait vide.
La coloration des barres de nom était morte après chaque connexion et chaque rechargement : l'interrupteur indiquait « activé », mais plus rien n'était coloré tant qu'on ne le basculait pas deux fois à la main.
La barre de lecture restait invisible en combat - précisément quand elle est la plus utile.
La barre de lecture n'affichait que le nom d'une entrée de menu, pas la valeur lue ensuite - les états d'interrupteur et les quantités étaient audibles mais pas lisibles.
Une fenêtre fermée pouvait être annoncée de nouveau plus tard - typiquement le maître, bien après l'ouverture de la fenêtre de métier.
Le menu s'ouvrait parfois tout seul à son niveau le plus haut, au lieu de là où l'on voulait aller.
Avec deux fenêtres ouvertes en même temps, la fermeture de la seconde passait inaperçue.
Après la fermeture d'une fenêtre, ses entrées pouvaient subsister sous « Local ».
Maître : apprendre plusieurs sorts coup sur coup puis fermer la fenêtre juste après la laissait continuer à parler.
Hôtel des ventes : « utilisables uniquement » cachait précisément les objets de haut niveau recherchés dans la liste du scan complet.
Hôtel des ventes : la liste du scan complet filtrait par niveau et par qualité alors qu'aucun filtre n'était réglé - les marchandises, les composants et tout le gris manquaient.
Hôtel des ventes : une catégorie sans sous-catégories n'affichait rien d'autre que « Tout ».
Hôtel des ventes : un achat annulé pouvait détourner la recherche suivante et armer un achat que personne n'avait demandé.
Hôtel des ventes : une recherche interrompue par le chien de garde le dit désormais - une liste à moitié remplie ressemblait à un résultat terminé.
Nage et vol : le verrouillage de l'inclinaison met fin à la plongée involontaire
Simple : En nageant ou en volant, chaque virage automatique vers la balise tirait un peu
vers le bas - sur une longue distance, cela s'additionnait jusqu'à se retrouver sous
l'eau ou au sol sans s'en apercevoir. Désormais Sku verrouille l'inclinaison dès que
vous nagez ou volez : vous vous déplacez parfaitement à l'horizontale, et Espace et X
continuent de vous porter vers le haut et vers le bas. Et si quelque chose vous
incline malgré tout - par exemple l'approche automatique en interagissant avec un
PNJ - Sku vous redresse activement : à l'ouverture de la fenêtre du PNJ, peu après
chaque virage, et à chaque appui sur monter ou descendre. Activé par défaut ; qui
veut plonger à la souris en inclinant la vue (vision résiduelle) le désactive sous
Paramètres -> Général. S'y ajoute le nouveau raccourci « verrouillage de
l'inclinaison » (Ctrl+Maj+N) pour verrouiller et libérer manuellement.
Technique : Trois éléments. Premièrement, la variable console pitchlimit 0 plafonne
l'inclinaison de déplacement du personnage (la technique des addons LevelFlight et
FlightHUD) - posée durablement plutôt qu'autour de chaque impulsion de virage, car
c'est exactement cette course au basculement qui a fait échouer les tentatives
précédentes. pitchlimit s'écrit mais ne se lit pas ; chaque connexion rétablit donc
aveuglément la valeur par défaut 88, pour qu'un verrouillage ne reste jamais coincé.
Deuxièmement, le verrouillage ne fait que conserver ce qui est : un déplacement piloté
par le moteur, comme l'approche d'interaction, incline quand même le personnage, et
ensuite aucune touche du clavier ne permet de relever l'inclinaison. Le redressement
actif utilise précisément le transfert qui causait le bug à l'origine :
ResetView/SetView cale la caméra sur le préréglage par défaut connu, presque
horizontal, une impulsion mouselook le transfère au personnage, et le verrouillage
plafonne le petit reste à zéro. Les déclencheurs sont la fenêtre du PNJ, chaque
virage (différé de 0,5 s, car le moteur n'applique le transfert de cap qu'une frame
plus tard - une seconde impulsion immédiate annulerait le virage) et, via
hooksecurefunc, les fonctions monter/descendre JumpOrAscendStart, AscendStop,
SitStandOrDescendStart et DescendStop - quelles que soient les touches où elles sont
assignées. Troisièmement, un inclinomètre pour le journal : la vitesse horizontale
mesurée divisée par GetUnitSpeed donne le cosinus de l'angle de trajectoire - la
seule mesure que ce client offre (GetUnitPitch a été retiré au patch 7.1 et manque
ici aussi). Il mesure la trajectoire, pas l'inclinaison du corps, et ne sert qu'au
diagnostic.
Le curseur reste sur l'objet dans le sac
Simple : Faire un clic droit sur un objet de la liste des sacs - l'utiliser, l'équiper, le
vendre à un marchand, appliquer un enchantement - vous déposait sur la première entrée
de toute la liste. Pour les objets qui disparaissent aussitôt, cela se voyait à peine,
puisque la liste se réordonne de toute façon ; pour tout le reste, c'était simplement
une position perdue. Désormais le curseur reste sur l'objet sur lequel vous avez
travaillé, et quand l'objet quitte le sac, vous atterrissez sur son emplacement,
c'est-à-dire sur ce qui a pris sa place.
Technique : La cause n'a jamais été un décalage d'indices, mais la troisième ligne de la
macro de clic droit, « /script SkuCore:CheckFrames() ». Elle datait d'avant la
confirmation pilotée par les événements et s'exécutait AU MOMENT DE LA TOUCHE - donc
avant que l'action ait changé quoi que ce soit, et pour une action avec temps
d'incantation, plusieurs secondes avant. Elle reconstruisait donc des données périmées
et coûtait la position pour rien. Supprimée ; le rafraîchissement appartient à la
seule confirmation pilotée par BAG_UPDATE, qui ne reconstruit qu'une fois les sacs
réellement stabilisés et réaccroche alors le curseur par emplacement de sac et
identifiant d'objet. De plus, CheckFrames ne retient plus seulement le numéro de ligne
de l'entrée à travers une reconstruction, mais aussi son nom, qu'il recherche dans la
liste reconstruite - le numéro de ligne seul cesse d'être juste dès que la liste a eu
une autre forme entre-temps.
Enchantements, appâts et huiles : la liste se rafraîchit après l'incantation
Simple : Un enchantement, un appât de pêche, une huile d'arme, un kit d'armure ou un
désenchantement ne modifient les sacs qu'une fois l'incantation terminée, souvent cinq
secondes après le clic. Sku avait depuis longtemps renoncé : la liste restait périmée
et le curseur se trouvait quelque part. Désormais Sku attend la fin de l'incantation,
annonce ensuite l'objet une fois et y laisse le curseur. Si vous naviguez vous-même
entre-temps, vous gardez votre propre position - Sku ne s'en mêle plus.
Technique : La fenêtre de confirmation ouverte par le clic droit expirait au bout de
2,5 secondes. UNIT_SPELLCAST_START/-CHANNEL_START repoussent maintenant son échéance
jusqu'à la fin de l'incantation tout en conservant l'ancre saisie au clic, de sorte
que le réaccrochage retombe exactement sur l'objet traité. Volontairement sans liste
de sorts : une fenêtre ouverte est en elle-même la preuve que cette incantation
appartient à l'action. Deux garde-fous stricts, tous deux appris d'un cas réel :
l'incantation doit suivre le clic dans la seconde (« /use » la déclenche dans la même
image), et le report n'a lieu qu'une seule fois. Sans eux, une pêche de 22 secondes
repoussait l'échéance devant elle sans fin et la fenêtre ne mourait jamais.
La boîte de dialogue de confirmation ne coûte plus la position
Simple : Appliquer un enchantement sur un objet déjà enchanté déclenche la question de
Blizzard « Remplacer l'enchantement existant ? ». Après « Oui » ou « Non », on se
retrouvait tout en haut de la hiérarchie des sacs, sur « Sac 1 » au lieu de l'objet.
Désormais, les deux réponses ramènent à l'objet, et il est annoncé une fois.
Technique : La boîte de dialogue est une fenêtre modale entre le clic et son effet, et
elle cassait la position doublement : l'échéance de 2,5 secondes expirait pendant
qu'elle était affichée, et Sku ancre le menu DANS la boîte, si bien qu'à sa fermeture
il ne restait rien où revenir - l'entrée générique prenait alors la seule fenêtre
ouverte et atterrissait en haut de la liste des sacs. SkuBagPostActionPopupGate
maintient la fenêtre ouverte tant que la boîte est affichée (jusqu'à 30 secondes) et
déclenche la confirmation à sa fermeture. C'est le bon outil précisément parce qu'il
couvre les deux réponses : « Non » ne produit ni incantation ni changement de sac,
rien d'autre ne pourrait donc jamais replacer le curseur. Tant que la boîte est
affichée, la confirmation s'abstient entièrement - sinon un changement de sac sans
rapport (du butin qui arrive, un échange qui se conclut) arracherait le curseur hors
de la boîte.
Le filtre de saisie de la liste des sacs se lève à nouveau
Simple : Taper un nom dans la liste des sacs la filtre sur les correspondances. Ce filtre
ne se levait plus dès que vous aviez utilisé l'objet filtré : même flèche gauche puis
droite ne ramenait pas la liste complète, seule la fermeture des sacs y parvenait.
Désormais, chacune de ces touches lève à nouveau le filtre, comme vous en avez
l'habitude. Quand rien ne correspond au filtre, la liste reste vide comme avant et les
flèches ne font que répéter le filtre saisi - c'est précisément ainsi que l'on entend
qu'il n'y a aucun résultat.
Technique : SkuOptions:ClearFilter n'oubliait que le texte saisi ; la liste d'enfants
filtrée installée par ApplyFilter restait dans l'arbre jusqu'à ce que quelque chose la
remplace intégralement - et un niveau construit à partir des fenêtres du jeu, comme
les sacs, ne peut pas se rafraîchir sur place, il lui faut une reconstruction
complète. Masqué pendant des années par le CheckFrames forcé de la macro (voir
ci-dessus), cela est apparu dès qu'une action ne modifie pas du tout les sacs.
ClearFilter restaure maintenant réellement la liste, chaînages avant et arrière
compris, et ce sur le nœud qu'il a filtré et non sur celui qui a le focus.
Échap ne fermait pas le menu lorsqu'on se tenait juste sur un objet de quête
Simple : Se tenir à environ zéro mètre devant un objet de quête et fermer le menu avec
Échap faisait aussitôt remonter la fenêtre de quête - sans fin. À cet endroit, le menu
ne pouvait pratiquement plus être fermé. Désormais les fenêtres de jeu ouvertes sont
fermées d'abord, et le menu lui-même ensuite.
Technique : Le menu se cachait d'abord et traitait ensuite les fenêtres d'interaction
ouvertes. Le cadre de quête était donc fermé alors que le jeu signalait toujours
l'objet à portée, et il se rouvrait immédiatement. L'ordre est maintenant inversé et
protégé par un compteur que « /skucheck menu » relit : une violation de cet ordre se
remarque sans que quiconque ait à se tenir par hasard exactement à cet endroit.
Moins de reconstructions en double à la fermeture d'une fenêtre
Simple : Un seul Échap déclenchait jusqu'à six reconstructions complètes des données du
menu, parce que plusieurs fenêtres se ferment en même temps et que la fenêtre de quête
de Blizzard à elle seule masque quatre sous-panneaux. Cela coûtait un temps
perceptible. Désormais, exactement une reconstruction s'exécute par image. Par
ailleurs, la liste des sacs tenue pour le combat pouvait se périmer lorsqu'une fenêtre
de quête était fermée alors que les sacs étaient ouverts - un défaut qui ne serait
apparu que plus tard, en combat.
Technique : Les chemins de masquage passent par SkuCore:CheckFramesCoalesced, dont le
verrou s'ouvre une fois par image ; le passage effectif est de toute façon différé de
0,01 s et lit l'état vivant, il voit donc exactement ce qu'aurait vu le dernier appel
de la rafale. Le côté OnShow est volontairement laissé de côté. La comptabilité de
GENERIC_OnClose a reçu son propre verrou : elle dépendait auparavant de la valeur de
retour de ce même verrou partagé, et comme les sous-panneaux de quête se déclenchent
en premier, ils le consommaient et la comptabilité - dont le rafraîchissement de la
liste des sacs de combat - était sautée.
Synthèse vocale : les annonces ne restent plus muettes
Simple : Pendant la navigation, la voix se taisait régulièrement pendant un moment -
une touche fléchée ne lisait tout simplement pas l'entrée, dans le calme et sans
que rien d'autre ne tourne. C'était le plus reproductible quand on filtrait une
liste jusqu'à un seul résultat puis qu'on allait vite et venait entre le filtre et
ce résultat : après quelques passages, le résultat restait définitivement muet,
même en attendant une seconde. Les deux sont corrigés.
Technique : Deux causes indépendantes, toutes deux sous la file d'attente - Sku avait
transmis proprement chaque annonce ; sur une capture de 95 minutes, chaque appui
produisait exactement un SpeakText avec un STARTED.
D'abord, la même ligne était réémise jusqu'à quatre fois en une seconde, parce
qu'une vérification de fenêtre superflue réannonce l'entrée de menu ; 95 des 1512
énoncés étaient de telles répétitions. Chacune apporte son propre
« queuereset », donc un StopSpeakingText, et celui-ci atteint bel et bien le
lecteur d'écran - la ligne était donc coupée en plein mot puis reprise, et quand
le renvoi se perdait en chemin il ne restait rien. Le garde-fou anti-doublon
existant ne pouvait rien y faire : il dépend de
VOICE_CHAT_TTS_PLAYBACK_FINISHED, et via le pont vers le lecteur d'écran STARTED
et FINISHED reviennent dans la même image - il y est donc toujours vide et ne peut
jamais se déclencher. Un garde-fou temporel a été ajouté : il écarte une ligne
identique immédiatement suivante dans la seconde, avec son queuereset ; n'écarter
que le texte annulerait quand même la ligne en cours sans rien mettre à sa place.
Il ne se compare qu'à la ligne transmise juste avant, il ne peut donc pas avaler
une vraie relecture, et il ne s'applique que si la première copie a réellement
commencé à être jouée.
Ensuite, le casseur de cache ne fonctionnait plus. Le client range la parole
synthétisée par texte et la rejoue lors d'une répétition sans rappeler la voix -
via un pont, cette relecture est silencieuse. La parade était jusqu'ici un
tournant, mais le client retire le balisage et n'indexe que sur le texte
parlé : dans la capture, chaque énoncé portait sa propre marque et le résultat se
taisait quand même. Le casseur d'origine faisait varier de vrais caractères et
fonctionnait ; le passage à une marque devait ménager la prosodie et a
discrètement ramené le défaut même contre lequel il avait été écrit. On rajoute
donc du vrai texte : une suite tournante de 1 à 64 espaces insécables après le
dernier mot, où elle n'est ni audible ni capable de colorer la prosodie.
Rassasié : une seule entrée dans le sélecteur de sorts
Simple : Qui voulait bâtir une aura sur le buff Rassasié trouvait dans le sélecteur de
sorts deux entrées prononcées toutes deux « Rassasié ». Une seule pouvait se
déclencher, l'autre était une fiche morte - laquelle on attrapait relevait du pur
hasard et ne s'entendait pas. Il n'y a plus qu'une entrée, et c'est la bonne. Une
aura déjà enregistrée pointant sur la mauvaise est basculée une fois à la prochaine
connexion et fonctionne ensuite.
Technique : Depuis la v43.0, les auras sont tenues par l'identité anglaise d'un sort,
afin que la même aura convienne dans toutes les langues. Or, dans la base de sorts
livrée, dix lignes portent le nom anglais ENTRE GUILLEMETS - 44097 à 44106 ainsi que
69559 et 65365 portent le nom entouré de deux guillemets, là où les 65 autres le
portent sans. Cela
créait une seconde identité derrière un seul et même nom localisé, et la
désambiguïsation du menu ajoute dans ce cas l'identité anglaise : une entrée se
lisait « Rassasié (Well Fed) », l'autre pareil mais avec deux guillemets
supplémentaires autour de Well Fed. La synthèse vocale ne lit pas les
guillemets : les deux entrées sonnaient donc à l'identique. Rassasié est le seul des
26 650 noms localisés du jeu de données dont les deux identités ne diffèrent que par
ces caractères. Le nom est désormais déguillemeté lors de la formation de l'identité,
et des DEUX côtés - dans la liste de valeurs et à chaque résolution d'un événement en
cours - car ne changer qu'un côté produirait une valeur enregistrée qu'aucun
événement ne peut plus atteindre. Seuls les noms encadrés de guillemets aux DEUX
extrémités sont traités ; les 17 lignes restantes avec des guillemets à l'intérieur
du nom (Goblin "Boom" Box, parfum « TRIUMPH ») les conservent, car ils y font partie
du nom. Une migration unique bascule les auras enregistrées.
Les enchantements d'arme sont reconnaissables dans le sélecteur de sorts
Simple : Un appât de pêche, une pierre à aiguiser ou une huile d'arme n'est pas un buff
mais un enchantement posé sur l'arme. Il n'apparaît dans aucune liste de buffs du
jeu et ne déclenche aucun événement d'aura - une aura sur « événement = aura perdue
et nom du sort = Compétence Pêche +75 » ne pouvait donc jamais se déclencher, alors
même que l'entrée figure dans le sélecteur et sonne tout à fait normalement. De
telles entrées portent désormais la mention « (enchantement d'arme) » dans tous les
sélecteurs, afin que l'on entende de quoi il s'agit. Pour y réagir, on prend soit
l'événement « Enchantement d'arme expiré », soit la condition « Ton enchantement
d'arme main droite ». La mention n'est pas lue dans l'annonce d'une aura déclenchée.
Technique : La liste de valeurs de l'attribut « nom du sort » est la base de sorts
complète, y compris les lignes qui n'atteignent le joueur que sous forme
d'enchantement d'arme temporaire - l'appât est le sort 8084 derrière l'identifiant
d'enchantement 265. Les identités concernées sont maintenant déterminées lors de la
construction des listes (chaque identifiant de sort de l'identité est référencé par
la base des enchantements) et reçoivent la mention. Volontairement SIGNALÉES plutôt
que retirées : elles sont 395 et ne sont pas toutes inertes - Arme enflammée,
Bourreau et Force impie sont des procédures d'enchantement qui apparaissent bel et
bien sous ce nom exact dans le journal de combat. Les retirer aurait privé des auras
fonctionnelles de leur seul moyen de les nommer. La mention emprunte le même
mécanisme que la désambiguïsation anglaise et disparaît donc de nouveau dans
l'énoncé d'une aura déclenchée.
Les enchantements d'arme s'adressent comme des sorts
Simple : Pour un sort de dégâts sur la durée, on écrit « événement égale aura perdue » et
« nom du sort égale » - l'événement dit lui-même de quoi il s'agit. Pour un
enchantement d'arme, c'était impossible : l'appât ne pouvait être nommé que par
« ton enchantement d'arme main droite », qui est une question d'état et non
d'événement. Sous la forme « contient », une telle aura ne pouvait même jamais se
déclencher - quand l'événement arrive, l'enchantement a déjà disparu, la liste ne le
contient donc plus. Seule la forme inversée « ne contient pas » fonctionnait.
L'événement porte maintenant lui-même le nom de l'enchantement, et l'aura s'écrit
exactement comme pour un sort : « événement égale Enchantement d'arme expiré » et
« nom du sort égale Compétence Pêche +75 ». « ne contient pas » continue de marcher.
Technique : Les deux événements générés WEAPON_ENCHANT_REMOVED et WEAPON_ENCHANT_UPDATE
plaçaient le nom de la MAIN dans le champ 13 - et le champ 13 est celui du nom du
sort. Une condition sur « nom du sort » se comparait donc à « Main droite », et une
sortie « nom du sort » annonçait la main au lieu de l'enchantement. C'est désormais
l'enchantement qui s'y trouve : le champ 12 reçoit son identifiant de sort, le champ
13 son nom, l'un et l'autre par les mêmes résolveurs qui construisent la liste de
valeurs - la valeur enregistrée est la même que pour la condition d'enchantement.
Une expiration signale l'enchantement qui ÉTAIT là, c'est l'identité visée. Les
auras existantes ne peuvent pas devenir muettes : « Main droite », « Main gauche »,
« Haupthand » et « Nebenhand » n'apparaissent pas une seule fois comme nom de sort
dans les 49 117 lignes de la base de sorts ; une condition sur « nom du sort » ne
pouvait donc de toute façon jamais correspondre sur cet événement. La main n'est pas
perdue, elle passe dans son propre champ avec sa propre condition et sa propre sortie
(« main de l'enchantement d'arme »), enregistrée sous forme de jeton neutre afin
qu'une aura partagée désigne la même main dans toutes les langues. Ce qui n'a
volontairement PAS été fait, c'est l'autre voie évidente - remplir la liste d'état
lors d'une expiration avec l'enchantement sortant : la liste est partagée, elle
mentirait à toutes les autres auras durant ce passage, réarmerait une barrière
« ne contient pas » et produirait ainsi des annonces en double, et elle
contredirait la durée restante, délibérément forcée à 0 lors d'une expiration. De
plus, /skucheck auras signale désormais la combinaison « événement d'expiration plus
contient » comme une aura qui ne peut jamais se déclencher. Corrigé au passage :
lorsque seule la main gauche changeait, l'événement était tout de même signalé comme
la main droite.
La fin d'un enchantement d'arme est détectée immédiatement
Simple : Le jeu n'annonce pas qu'un appât de pêche ou un poison a expiré - Sku regarde
toutes les 0,25 seconde. L'annonce arrivait donc jusqu'à un quart de seconde trop
tard. Elle arrive maintenant au moment de l'expiration.
Technique : La durée restante d'un enchantement est connue dès l'instant où on le voit -
GetWeaponEnchantInfo renvoie le temps restant en millisecondes. Le ticker en retient
la prochaine expiration, et le pilote de frame compare un nombre par frame puis
appelle exactement le ticker joueur habituel. C'est la même construction que le
planificateur d'échéances de durée introduit en v43.0 et cela ne coûte aucune
interrogation supplémentaire. Les annonces en double sont exclues, car le ticker
n'émet que si son état mémorisé a réellement changé. Volontairement NON fait : passer
le ticker à chaque frame - ce serait quinze fois plus d'interrogations pour le même
gain.
Basculer un réglage dans une liste filtrée annonce de nouveau le nouvel état
Simple : Restreindre une liste avec le filtre de saisie puis appuyer sur Entrée sur une
entrée activé/désactivé retire le filtre - c'est voulu. L'entrée était bien
basculée, mais on ne l'entendait pas : le curseur sautait en tête de liste et c'est
la première entrée qui était annoncée, pas le nouvel état. Dans les sorties d'auras,
l'aperçu sonore de la première entrée se déclenchait en plus. On reste désormais sur
l'entrée que l'on vient de basculer et on entend son nouvel état.
Technique : Entrée retire le filtre AVANT d'exécuter l'action - le nœud de menu appelle
ApplyFilter("") pour cela. Son nettoyage se terminait par un OnFirst() systématique
sur le niveau, le curseur partait donc en tête de liste dans TOUS les cas. La
bascule elle-même n'a jamais été en cause : elle agit sur le nœud lui-même
(actionInPlace), pas sur le curseur. Seul ce qui était lu ensuite était faux - et
OnFirst déclenchait au passage le OnEnter de la première entrée, c'est-à-dire
l'aperçu sonore pour les sons d'auras. ApplyFilter("") suit maintenant la même
règle que ClearFilter : la liste restaurée contient les MÊMES objets de nœud, un
curseur posé sur une vraie entrée reste donc valide ; seule la ligne artificielle
« Filter; » n'y a plus sa place, et c'est pour elle seule que le OnFirst()
complet, OnEnter compris, continue de s'exécuter, afin de ne pas perdre l'aperçu
sonore à l'arrivée. Sans filtre actif, ApplyFilter("") n'a toujours rien fait -
d'où le fait que basculer dans une liste non filtrée n'a jamais posé problème ; les
deux cas se comportent désormais de la même façon. Effet de bord de la même règle :
Retour arrière laisse maintenant le curseur sur l'entrée où il se trouvait au lieu
de sauter en tête de liste.
Navigateur de donjons : accepter une invitation ne mène plus au navigateur
Simple : Si vous étiez inscrit dans le navigateur de donjons et acceptiez ensuite une
invitation de groupe, vous vous retrouviez dans le navigateur sans avoir appuyé sur
la moindre touche : le menu se rouvrait tout seul et le curseur se posait sur « Non
inscrit ». Pour les joueurs voyants, rien ne se passe à ce moment-là - la fenêtre de
recherche de groupe de Blizzard ne s'ouvre jamais lorsqu'on accepte une invitation.
Sku reste désormais silencieux lui aussi : vous restez là où vous étiez.
Technique : Deux chemins y menaient, tous deux sont fermés. (1) Dès que le serveur
supprime votre inscription - c'est exactement ce qu'il fait lorsque vous rejoignez un
groupe -, LFG_LIST_ACTIVE_ENTRY_UPDATE arrive, et le navigateur reconstruisait alors
son entrée de menu. Cette reconstruction se terminait par un SlashFunc vers son
propre chemin de menu, et celui-ci rouvre un menu FERMÉ et y descend. Elle ne
s'exécute plus que si le menu est ouvert ET que le curseur se trouve réellement dans
le navigateur de donjons - ce n'est qu'alors qu'il faut replacer les entrées
fraîchement construites sous le curseur. L'omettre ne coûte rien : l'entrée de menu
est « dynamic » et se reconstruit de toute façon à la prochaine descente. Ainsi,
aucun changement d'inscription que vous n'avez pas provoqué (expiration, modification
par le chef de groupe) ne peut plus vous arracher à vos sacs pour vous jeter dans le
navigateur. (2) Un tiers de seconde après la disparition de la fenêtre d'invitation,
le navigateur vérifie s'il doit se rouvrir. Il demandait seulement « une fenêtre de
recherche de groupe est-elle ouverte en ce moment ? » et en faisait une ouverture.
Désormais, l'état présent à l'ARRIVÉE de l'invitation est mémorisé, et c'est
exactement lui - et rien d'autre - qui est restauré. Si l'invitation vous a fait
rejoindre un groupe, il ne se passe plus rien du tout : ni ouverture, ni fermeture.
Nouveau raccourci : lire l'infobulle complète de la cible
Simple : lors d'un changement de cible, Sku annonce un ensemble fixe et court - nom,
niveau, élite ou rare, plus la deuxième ligne de l'infobulle avec le type de créature.
Tout ce qui se trouve en dessous dans l'infobulle était jusqu'ici inaccessible : la
ligne de faction, le marqueur JcJ et surtout ce que la compétence de chasseur
Connaissance des bêtes révèle d'une bête - sa famille, son régime et si elle est
apprivoisable. Le nouveau raccourci (par défaut : Ctrl+Maj+V) lit l'infobulle complète
à la demande. Il n'agit que lorsqu'on appuie dessus : l'annonce de cible habituelle
reste aussi courte qu'avant, et qui n'a pas besoin de ce détail ne l'entend jamais.
C'est aussi pourquoi il n'y a pas d'option pour cela - appuyer sur la touche EST la
demande. Sans cible fixe, la touche décrit la cible souple actuelle, ce qui permet
aussi de décrire quelque chose sur quoi on ne s'est pas encore engagé. Réassignable
dans Raccourcis clavier Sku, cible et ciblage souple.
Technique : l'idée, la démonstration et l'implémentation de référence viennent de ZenqFR,
dans l'addon compagnon SkuBeastLore (github.com/ZenqFR/Sku-BeastLore) ; la sortie
Connaissance des bêtes y a été testée sur un vrai chasseur. Ici la fonction est
reconstruite nativement sur les interfaces de Sku, pour qu'aucun addon supplémentaire
ne soit nécessaire. Les lignes de Connaissance des bêtes sont produites par le client
lui-même dans l'infobulle d'unité ; aucun Lua de l'interface Blizzard ne les construit
et aucune API ne les renvoie séparément - lire l'infobulle est le seul moyen d'y
accéder. C'est aussi pourquoi une option distincte pour la seule information de
chasseur n'est pas possible : ces lignes ne pourraient être isolées que par une
comparaison de texte dépendante de la langue. SkuMob:OutputTargetTooltip résout
target, softenemy, softfriend puis softinteract dans cet ordre et lit via le
SkuScanningTooltip partagé de Sku. Comme ce frame est partagé - sacs, hôtel des
ventes, liens de discussion et textes de quête lisent le même -, il est vidé avant ET
après, et son propriétaire restauré exactement comme SkuCore le règle à la connexion.
Suivre une quête et retirer le suivi
Simple : le menu de chaque quête se termine désormais par une entrée « Suivre la quête ».
Entrée la bascule, le curseur reste en place et le nouvel état est annoncé aussitôt -
« Suivre la quête;Activé » ou « Suivre la quête;Désactivé ». Suivie signifie : le
marqueur propre à WoW sur la minicarte et la carte du monde. Qui garde un reste de
vue trouve ainsi ses objectifs plus vite ; qui ne voit rien n'en tire rien. L'entrée
peut donc être désactivée entièrement - sous Réglages Sku, SkuQuest, « afficher
l'entrée Suivre la quête » ; elle est active par défaut. Elle n'apparaît que pour les
quêtes de votre propre journal, car une quête de la base de données de quêtes n'est
pas encore la vôtre. Deux limites viennent du jeu lui-même : The Burning Crusade
n'autorise que cinq quêtes suivies à la fois, et une quête sans aucun objectif ne
peut pas être suivie. Dans les deux cas, Sku annonce le message du jeu et l'état
reste inchangé.
Technique : l'idée et l'implémentation de référence viennent de ZenqFR, dans l'addon
compagnon SkuQuestNearby (github.com/ZenqFR/Sku-QuestNearby) ; avec l'accord de ZenqFR, la
fonction est ici reconstruite nativement sur les interfaces propres à Sku, afin
qu'aucun addon supplémentaire ne soit nécessaire. L'entrée est placée dans le
CreateQuestSubmenu local au fichier plutôt que sur un hook du wrapper public - c'est
pourquoi toutes les vues de quête en bénéficient : journal de quêtes, base de données
de quêtes et menus de quête de SkuChat. Il n'y a pas de C_QuestLog sur 2.5.6 :
AddQuestWatch, RemoveQuestWatch et IsQuestWatched prennent un INDEX du journal de
quêtes, pas un identifiant de quête, et cet index se décale à chaque quête acceptée
ou rendue. Sku tient donc une correspondance identifiant vers index, revérifie
l'index mémorisé à chaque lecture et abandonne la correspondance lors des événements
de quête ; sans ce cache, la liste « base de données de quêtes, toutes » ferait un
parcours complet du journal pour chaque quête. L'activation passe par
AutoQuestWatch_Insert avec QUEST_WATCH_NO_EXPIRE, pour que la quête entre dans
QUEST_WATCH_LIST et qu'aucun minuteur d'expiration ne l'en retire ; la désactivation
supprime d'abord la ligne de QUEST_WATCH_LIST puis seulement appelle
RemoveQuestWatch - exactement comme le chemin de clic de Blizzard, sinon une quête
depuis longtemps non suivie continue de compter dans la limite de cinq.
QuestWatch_Update ne s'exécute qu'en dehors du combat, car il finit par déplacer des
cadres protégés ; le prochain QUEST_LOG_UPDATE s'en charge de toute façon.
Nouveau raccourci « Cibler la créature de quête la plus proche » (Alt+H)
Simple : Une pression cible une créature liée à un objectif de quête encore ouvert -
y compris une créature qu'on n'a pas à tuer, mais qui se contente de faire tomber
un objet requis. Exemple : pour « Essences pour les moteurs », le journal ne nomme
que l'objet ; la touche cible malgré tout le Spectre de mana qui le fait tomber.
Si rien ne correspond à portée, Sku annonce « aucune cible de quête à portée » ;
si le journal ne contient rien de pertinent, « aucune cible de quête dans le
journal ». La touche est sur Alt+H par défaut et se réassigne dans les raccourcis
Sku, « Ciblage et ciblage souple ».
Technique : L'idée et l'implémentation de référence viennent de ZenqFR, dans l'addon
compagnon SkuQuestTarget (github.com/ZenqFR/Sku-QuestTarget) ; la fonction a été
reconstruite nativement avec l'accord de ZenqFR, pour qu'aucun addon supplémentaire ne
soit nécessaire. Les candidats proviennent du journal de quêtes via
SkuQuest:GetQuestTargetIds ; les objectifs de type objet sont remontés jusqu'aux
créatures qui les font tomber (npcDrops, et un niveau plus loin itemDrops). La
recherche commence par la zone où l'on se trouve - une créature sans point
d'apparition sur place ne peut pas être à portée de ciblage et ne doit pas occuper
une place dans la macro ; ce n'est que si rien n'est enregistré pour la zone que le
tri se fait sur tout le continent. La distance retient le point d'apparition
enregistré le plus proche, et non le premier de la liste. La macro est multiligne,
et une particularité du jeu décide du résultat : chaque ligne qui correspond
s'exécute et chaque correspondance écrase la précédente - c'est donc la DERNIÈRE
qui gagne. Les candidats sont donc retenus du plus proche au plus lointain, afin
que le budget de longueur ne coupe que le lointain, mais écrits à l'envers, pour
que le plus proche soit la dernière ligne. La macro est reconstruite à chaque
pression hors combat ; en combat, c'est le dernier texte construit qui part.
« Ennemi suivant en combat » trouve aussi ce qu'on ne regarde pas (Ctrl+H)
Simple : La touche ne trouvait que ce que l'on regardait déjà - le même ennemi à cinq
mètres derrière soi ne donnait rien. Ce n'est pas un réglage, c'est le
fonctionnement du ciblage par tabulation du jeu. Sku retient donc désormais les
noms des créatures hostiles croisées et les essaie en plus par leur nom - et un nom
fonctionne dans toutes les directions. En pratique : ce que l'on a vu, combattu ou
ciblé une fois reste accessible de partout pendant deux minutes, même dos tourné.
La recherche repart par ailleurs de l'ennemi le plus proche à chaque pression au
lieu de continuer le cycle. La touche est sur Ctrl+H par défaut.
Technique : L'approche est reprise du SkuQuestTarget de ZenqFR, qui emprunte la même
voie pour sa fonction secondaire. /targetenemy est la tabulation et cherche dans le
cône de vue - rien ne peut changer cela. « /tar nom » interroge en revanche la
liste de noms du client et ne dépend pas de l'orientation, mais il lui faut un nom,
et aucune interface ne permet d'énumérer les unités situées derrière soi. Sku
collecte donc les noms en continu depuis quatre sources : les plaques hostiles, sa
propre cible, l'unité sous le curseur et la liste d'ennemis du moniteur de combat.
La collecte tourne à la seconde tant que la touche est assignée, car une plaque
n'existe que tant que la créature est en vue - c'est-à-dire précisément quand on
n'a pas besoin de la touche. Seuls les noms y sont collectés ; la mesure de
distance, coûteuse, ne tourne qu'une fois par pression. Les noms retenus durent
deux minutes. La macro est « /cleartarget », puis « /targetenemy », puis les noms
retenus du plus lointain au plus proche - la dernière ligne qui correspond
l'emporte, donc la plus proche.
Nouvelle liste « Quêtes à proximité »
Simple : Le menu de quête possède désormais une troisième entrée, après « Quêtes
actuelles » et « Base de données des quêtes » : « Quêtes à proximité ». Elle
contient tout le journal de quêtes en UNE seule liste - quêtes en cours et quêtes
terminées mélangées - triée selon la distance de ce qui compte actuellement pour
chaque quête. Chaque entrée annonce d'abord la distance, puis le point cardinal,
puis le type, puis le nom de la quête : « 180 Mètre ; nordest ; objectif de quête ;
Kobolds voleurs » ou « 340 Mètre ; sud ; Rendre la quête ; Le chef de la meute ».
Pour une quête aux objectifs encore
ouverts, c'est la distance jusqu'à l'objectif le plus proche ; pour une quête
signalée terminée, la distance jusqu'au lieu de remise. « Quêtes actuelles »
regroupe selon les en-têtes du journal et trie donc par zone - la nouvelle liste
répond à l'autre question : qu'est-ce qui est le plus proche d'ici. Entrée ouvre le
même menu de quête que partout ailleurs, points de passage et entrée « Suivre la
quête » compris. La liste est DÉSACTIVÉE par défaut et s'active sous Paramètres
Sku, SkuQuest, « afficher l'entrée Quêtes à proximité » : elle recalcule tout le
journal de quêtes contre la base de données de spawns de Sku à chaque ouverture, et
seul celui qui s'en sert devrait le payer. Là où les données de Sku ne donnent
aucun lieu, l'entrée indique « Lieu inconnu » au lieu d'une distance et passe en
fin de liste - elle ne disparaît pas.
Technique : Idée et implémentation de référence de ZenqFR, tirées de l'addon compagnon
SkuQuestNearby (github.com/ZenqFR/Sku-QuestNearby) ; reconstruite ici nativement
avec l'accord de ZenqFR afin qu'aucun addon supplémentaire ne soit nécessaire - en
particulier sans Questie et sans GatherMate2. Le calcul vit dans le nouveau fichier
sans état SkuQuest/Proximity.lua. Deux étages, et l'ordre est l'essentiel : d'abord
le point de spawn enregistré le plus proche DANS la zone où l'on se trouve - la
sélection tourne à bas coût en coordonnées de carte, seul le gagnant est converti
une fois en coordonnées du monde ; ensuite seulement, si cela ne donne rien, le
minimum sur toutes les zones du même continent. Un autre continent n'est pas une
distance imprécise, c'est une absence de distance, et il reste dehors. Les
objectifs d'objet sont ramenés aux créatures et objets du monde qui les font
tomber, les objectifs d'exploration à leurs coordonnées enregistrées. Contrairement
au chemin des points de passage, TOUTES les listes d'objectifs d'une quête sont
lues, pas seulement la première non vide - une quête « tuez huit loups ET
récoltez cinq peaux » n'afficherait sinon que les loups. Le filtrage porte
uniquement sur le TYPE d'objectif : quels types sont encore ouverts vient de
l'affichage de progression de Blizzard lui-même (« monster », « item », « object »),
et il correspond exactement aux trois listes ; comparer une ligne de progression à
une entrée de base de données précise relèverait de la devinette, car aucun des
deux ordres n'est lié à l'autre. Douze identifiants au maximum sont évalués par
quête et par type - la limite ici est le budget de script des royaumes hardcore,
pas la dernière décimale. Le point cardinal provient des mêmes coordonnées du
monde, via la même fonction qu'utilisent les listes de points de passage et le
scanner de minicarte. Volontairement un point cardinal et non une position
horaire : une position horaire est relative à l'orientation du regard et serait
fausse dès qu'on se tourne - or la liste est construite une fois puis parcourue.
Base de données des quêtes : « Commence dans la zone » sans niveau intermédiaire
Simple : Sous « Base de données des quêtes, Commence dans la zone » se trouvait
exactement une entrée, « Par distance », et seulement à l'intérieur les quêtes. Un
niveau où il n'y a rien à choisir n'est qu'une pression de touche. La liste se
trouve désormais directement sous « Commence dans la zone ». Cette liste était par
ailleurs cassée : elle s'interrompait après la première quête, on obtenait donc
exactement une entrée au lieu de toutes les quêtes de la zone.
Technique : Le niveau intermédiaire venait de ce qu'un second tri (« par difficulté »)
devait un jour s'installer à côté ; ce chantier est resté commenté à côté pendant
des années et n'a jamais été construit. L'interruption venait d'une ligne
« tcount = tcount + 1 » dans la construction de la liste : tcount n'y était pas une
variable locale mais une globale jamais définie, l'incrémentation levait donc une
erreur sur nil. Une erreur dans la construction des enfants d'un menu est avalée -
la construction se terminait donc silencieusement après la première entrée. Le
compteur n'était lu par rien ; il est supprimé plutôt qu'initialisé.
Tout le contenu de la banque en une seule liste, et la banque reconnue aussi sous Bagnon
Simple : Le menu des sacs avait « tous les sacs » - une liste à plat sur l'ensemble des
sacs, triée par nom. La banque n'avait pas d'équivalent : devant une banque ouverte
le menu affichait bien « Banque » et « Banque Sac 1 » à « Banque Sac 7 », mais
chercher quelque chose obligeait à entrer dans chaque compartiment l'un après
l'autre. Désormais, tant que la banque est ouverte, une nouvelle entrée « toute la
banque » se place juste après « tous les sacs » : tout ce qui se trouve dans la
banque en une seule liste, avec le même filtre à la frappe. Ctrl+Entrée y déplace
un objet vers vos sacs - exactement comme il le faisait déjà dans chaque sac de
banque. De plus, Sku détermine maintenant si la banque est ouverte à partir des
messages du jeu lui-même, et non plus de la fenêtre de Blizzard. Qui utilise un
addon de remplacement des sacs comme Bagnon, lequel masque cette fenêtre, n'avait
jusqu'ici aucune banque dans le menu - Sku la croyait fermée et faisait disparaître
tous les compartiments de banque. Aucun addon-pont n'est plus nécessaire pour cela.
Technique : La suggestion vient de l'addon compagnon SkuBagnonBridge de ZenqFR
(github.com/ZenqFR/Sku-BagnonBridge), qui comble la même lacune à sa manière et
reconnaît lui aussi la banque via les événements du jeu ; avec l'accord de ZenqFR,
la part qui se passe de tout addon supplémentaire est construite ici nativement. La
liste à plat se limitait aux sacs 0 à 4 ; les conteneurs de banque (-1, -3, 5 à 11)
n'y étaient jamais copiés. Ils sont désormais rassemblés dans une liste propre,
délibérément séparée de celle des sacs : SkuCore.combatBagOrder en dérive - le
miroir utilisé en combat - et celui-ci ne peut utiliser que des emplacements de
sac. Les entrées de la nouvelle liste sont des copies des mêmes entrées par
emplacement et emportent le sac et l'emplacement, si bien que toutes les actions
existantes fonctionnent sans changement. L'état de la banque tient maintenant à
BANKFRAME_OPENED et BANKFRAME_CLOSED : tous deux proviennent de l'interaction avec
le banquier elle-même et sont indépendants de qui dessine la fenêtre. Il est
réinitialisé au chargement et après /reload, et l'ancienne vérification de la
fenêtre est conservée à côté - la détection ne peut donc que reconnaître plus de
cas qu'avant, jamais moins.
La suppression d'un point de passage personnel effaçait la mauvaise entrée
Simple : supprimer un point de passage que vous aviez créé vous-même ne retirait pas son
propre identifiant d'une table de recherche interne, mais celui d'un autre point de
passage. Et « /skucheck wp » signalait quatre erreurs sur les quatre points de
passage rapides alors que tout allait bien pour eux.
Technique : le wpId d'un point de passage est son identité - la table des liaisons est
indexée dessus -, pas une description de l'endroit où il se trouve. L'identifiant de
zone qui y est encodé date du moment de la construction du cache et ne bouge plus
quand un point de passage personnel se déplace ; c'est exactement ce qui arrive aux
quatre points de passage rapides à chaque écran de chargement, puisqu'ils sont alors
replacés sur la position du joueur. La vérification reconstruisait l'identifiant à
partir de l'areaId actuel de l'enregistrement et comparait - elle utilise désormais
la zone encodée dans l'identifiant lui-même. Le chemin de suppression construisait
l'identifiant à partir de « .areaid » (le champ s'appelle areaId), donc avec la zone
1 au lieu de la vraie, et effaçait ainsi l'entrée de l'enregistrement à qui cette
clé appartient réellement ; il utilise maintenant l'identifiant stocké.
La saisie du chat est devenue utilisable
Simple : La ligne de chat était le seul champ de saisie de Sku dans lequel on tapait à
l'aveugle. Il y avait un son à l'ouverture et un à la fermeture, rien d'autre - pas
d'écho des caractères, pas de lecture sous le curseur, et aucune indication sur la
destination réelle du message. Elle se comporte désormais comme tout autre champ de
saisie de Sku. Chaque caractère tapé ou effacé est prononcé, les flèches gauche et
droite lisent le caractère sous le curseur et, avec Ctrl, le mot entier, la flèche
haut rappelle les derniers messages envoyés - pour les renvoyer ou les modifier - et
annonce le canal avec eux, car une ligne rappelée ne montre pas où elle irait. Le
canal est aussi annoncé à l'ouverture et à chaque changement, mais volontairement pas
pour « Dire » : c'est le cas inoffensif, et l'annoncer chaque fois ne serait que du
bruit. Si l'annonce ne convient pas à votre façon de faire, elle se désactive dans
les réglages du chat ; elle est activée par défaut. La désactiver retire aussi le
canal devant une ligne rappelée avec la flèche haut.
Un objet dans le chat compte pour un seul caractère à la lecture au lieu de
soixante-cinq caractères de balisage. Et une commande mal tapée comme « /tets » dit
maintenant « commande inconnue » au lieu de ne rien faire.
Technique : L'écho clavier des champs propres à Sku s'attache maintenant aussi aux champs
étrangers via SkuOptions:AttachInputEcho. Trois choses y diffèrent : l'attache se
fait avec HookScript et non SetScript, car le champ de Blizzard porte ses propres
gestionnaires d'historique et de complétion de noms ; le modèle pose ignoreArrows,
d'où des flèches simples envoyées au jeu et un curseur de texte exigeant ALT - ce qui
est inversé ; et l'armement se fait sur le focus, car un champ étranger n'a pas de
paire ouverture-fermeture à laquelle accrocher l'écho. Le canal est lu dans l'en-tête
de la saisie plutôt que reconstruit depuis chatType - l'en-tête est déjà traduit et
couvre les cibles de chuchotement, les canaux numérotés et les bascules propres à
Blizzard en plein appel. Son annonce est différée et tient dans un seul emplacement,
car tout changement de canal est déclenché par une frappe, et l'écho de cette frappe
éjectait sinon l'annonce de la file avant même qu'elle ne soit prononcée.
Les liens d'objet sont lus par leur nom, pas en codes de couleur
Simple : Un objet - dans le chat, dans le courrier, dans le butin - était lu comme une
longue suite de codes de couleur, d'identifiants et de deux-points au lieu de son nom.
Technique : La sortie vocale remplaçait d'abord chaque barre verticale par une espace,
PUIS appelait la fonction qui retire les codes de couleur, les liens et les textures.
Tous ses motifs cherchent une barre verticale, qui n'existait plus à ce moment - ils
ne correspondaient donc à rien depuis toujours. Ordre inversé ; le remplacement
général n'est plus qu'un filet de sécurité derrière.
Base de données des quêtes : « Commence dans la zone » indique aussi le point cardinal
Simple : La liste des quêtes qui commencent dans cette zone ne donnait jusqu'ici que la
distance du donneur de quête. Elle indique maintenant aussi, comme la liste
« Quêtes à proximité », dans quelle direction elle se trouve - d'abord la distance,
puis le point cardinal.
Technique : Les coordonnées mondiales du donneur de quête figuraient déjà dans cette
liste ; la direction vient de la même fonction que celle qu'utilisent les listes de
points de passage. Volontairement le point cardinal et non l'heure : un relèvement
horaire dépend de l'orientation du regard et serait faux dès qu'on tourne, alors que
cette liste est construite une fois puis parcourue. L'identifiant de quête d'une
entrée provient désormais directement de la ligne, au lieu d'une table de
correspondance indexée sur l'ancien libellé sans direction.
-------------------------------------------------------------------------------------------------------
Écho clavier : les caractères sont annoncés immédiatement
Simple : En tapant dans un champ de saisie, les lettres ne suivaient pas les doigts :
certaines étaient avalées, d'autres arrivaient nettement en retard, et les lettres
doubles comme dans « aller » ou « pomme » n'étaient souvent entendues qu'une fois.
Chaque caractère tapé arrive maintenant immédiatement, et une touche pressée deux
fois est prononcée deux fois.
Technique : L'écho passait jusqu'ici par la même file d'attente que chaque annonce de
menu, et cette file est conçue pour des annonces, pas pour des frappes. Elle
regroupait les caractères pendant un dixième de seconde et les prononçait en mode
écrasement - et écraser signifie ici : d'abord StopSpeakingText, puis attendre encore
un dixième de seconde pour que cet arrêt ne supprime pas justement la ligne à
laquelle il devait faire place. Les deux à la suite font environ 0,2 seconde par
frappe, et l'arrêt d'une frappe atterrissait régulièrement sur la suivante. S'y
ajoutait le nouveau garde-fou anti-doublon de cette version, pour lequel un caractère
isolé est un énoncé identique à lui-même - d'où les lettres doubles avalées. Les
caractères tapés empruntent désormais leur propre voie rapide : une seule place où la
frappe la plus récente l'emporte, transmission dans la même image, l'arrêt émis une
seule fois au début d'une série de frappes au lieu d'une fois par touche, et le
garde-fou anti-doublon ne voit plus du tout les caractères tapés. Contexte : un
lecteur d'écran n'a jamais besoin de tout cela, car son interface propose
l'interruption comme partie intégrante de la commande de parole - une seule
opération. L'interface du jeu ne le permet pas, Sku doit donc la reconstituer à
partir de deux appels qui peuvent se doubler. La nouveauté est de ne le faire qu'une
fois par série de frappes.
Une ligne redemandée par une touche est de nouveau prononcée
Simple : Redemander une ligne de menu par une touche produisait parfois du silence - la
synthèse vocale considérait la répétition comme superflue.
Technique : Le garde-fou anti-doublon écarte une ligne déjà prononcée à l'identique dans
la seconde précédente. C'est juste pour la reprise superflue d'une reconstruction de
menu, et faux pour le même texte que l'utilisateur vient de demander par une touche.
Les deux sont indiscernables par leur texte, et aucun délai ne les sépare - seul leur
déclencheur diffère. La gestion des touches du menu marque donc maintenant son
annonce comme déclenchée par une touche, et le garde-fou laisse toujours passer les
lignes marquées. Les répétitions superflues restent écartées, désormais quelle que
soit leur rapidité.
Les quêtes à objectifs mixtes montrent tous les types d'objectifs
Simple : Pour une quête comme « tuez 8 loups ET ramassez 5 peaux », l'entrée « Cible
de quête » du menu de quête ne montrait que le premier type d'objectif - le plus
souvent les ennemis ; les sources de l'objet à ramasser, ou un point
d'exploration, manquaient en silence. 95 quêtes étaient concernées. Tous les
types d'objectifs figurent désormais côte à côte dans une seule liste, et le
raccourci « Cibler la créature de quête la plus proche » connaît lui aussi les
deux moitiés de ces quêtes. Corrigé au passage : une quête qui n'a qu'un point
d'exploration et aucune autre donnée d'objectif reçoit enfin une entrée « Cible
de quête », et un point d'exploration irrésoluble ne produit plus une entrée qui
se contente d'annoncer « vide ».
Technique : SkuQuest:GetQuestTargetIds était une chaîne de elseif sur les listes
d'objectifs de la base de données (créatures, sinon objets, sinon objets à
ramasser, ...) et ne pouvait, par sa signature même, renvoyer qu'UNE liste avec
UN type. Remplacée par GetQuestTargetGroups : un tableau de groupes
{targets, type} - créatures fusionnées avec les crédits de mise à mort
dédupliqués, objets, objets à ramasser et, pour les seuls appelants objectives,
le point triggerEnd de la quête. Les sept sites d'appel sont convertis (menu de
quête, cache des points de passage, marqueurs de quête, touche de cible de
quête) ; l'ancien nom demeure comme enveloppe premier-groupe pour le port
WowVision. La branche item de GetResultingWps est en outre protégée contre les
identifiants d'objets inconnus - elle indexait sans vérification et laissait la
construction du menu mourir en un silencieux « liste vide ».
Base de données des quêtes mise au niveau actuel de Questie
Simple : La base de données de quêtes intégrée à Sku provient de Questie et datait
d'environ deux ans. Après la synchronisation, 33 quêtes dont la « Cible de
quête » restait vide ont de vraies cibles (les jeux de cartes de Sombrelune, les
quêtes répétables d'Ogri'la et de l'Œil pourpre, « Trouver le maître des
clés »), les six quêtes « Enquêter sur le Fléau » ont désormais des points
d'exploration, cinq quêtes manquantes ont été ajoutées, et environ 2000
enregistrements portent de petites corrections de noms, niveaux, donneurs de
quête et chaînes de prérequis. Vérifié : les corrections sont identiques pour
Era et TBC, la mise à jour est sûre sur les deux.
Technique : Une fusion sémantique, pas une copie d'octets (nouvel outil
dev/rework-docs/_refresh_questdata_from_questie.py, puis _db_convert.py) : le
schéma de Questie diverge à partir du champ 27, seuls les champs 1-26 sont
repris ; les champs propres à Sku, extraObjectives et skuData, sont préservés
par enregistrement. La convention requiredRaces (ancien masque toutes-races
2047, désormais 0 - identique pour la logique de Sku) compte comme égale dans
la comparaison, sans quoi 2117 enregistrements sans changement de contenu
auraient encombré le diff. Chaque identifiant de créature, d'objet et d'objet à
ramasser référencé a été validé contre les bases livrées (base + WotLK) ; un
enregistrement est resté à son ancien état pour cette raison.
Les quêtes d'événement n'apparaissent plus juste après le chargement
Simple : Dans les premières secondes après un /reload, des quêtes d'événements
mondiaux inactifs pouvaient apparaître dans « Commence dans la zone » - des
quêtes du Solstice d'été en août, par exemple. Une fois Questie complètement
chargé, elles disparaissaient d'elles-mêmes ; elles sont désormais masquées
aussi dans cette fenêtre.
Technique : Les deux barrières de disponibilité n'interrogeaient Questie qu'à
partir de Questie.started, et ce drapeau ne bascule qu'à la toute FIN de
l'initialisation de Questie. La liste noire statique de Questie,
QuestieCorrections.hiddenQuests - quêtes retirées du jeu plus TOUTES les quêtes
d'événement ; les actives ne sont dévoilées qu'après la réponse du calendrier
du serveur - existe pourtant bien plus tôt. IsQuestieUnavailable la lit
désormais directement avant started : une simple lecture de table ; le risque
d'empoisonnement de mémoïsation derrière la barrière started ne concerne que
IsDoable. La valeur « HIDE_ON_MAP » est ignorée comme le fait Questie lui-même,
et chaque masquage d'avant-démarrage écrit une ligne dprint dans le journal.
Les quêtes de donjon mènent à l'entrée au lieu de nulle part
Simple : Pour environ 300 quêtes, le donneur, la cible ou la remise se trouve
dans un donjon - « La vengeance de Vorrel » commence au Monastère écarlate,
« Willix l'importateur » se termine au Kraal de Tranchebauge. Début de quête,
Cible de quête et Livraison y restaient vides, car les instances n'ont pas de
carte du monde. Ces entrées affichent désormais « PNJ, entrée, nom du donjon,
zone » avec une route vers l'entrée du donjon - quêtes de champ de bataille
comprises (la vallée d'Alterac choisit la bonne entrée selon la faction). Les
sources d'un OBJET dans le même donjon sont regroupées en une seule entrée
« nom du donjon, entrée, zone » - « Le destin de Ramaladni » énumérait sinon
un à un 29 monstres de Naxxramas menant tous à la même porte ; les objectifs
d'élimination gardent leurs noms de monstres individuels.
Technique : Nouvelle table générée SkuDB.InstanceEntrances (depuis la table des
donjons de Questie, outil dev/rework-docs/_gen_instance_entrances.py) :
areaId d'instance -> entrées {zone parente, x, y, faction éventuelle}, y
compris les identifiants des ailes et une barrière GetBuildInfo pour les
déplacements d'entrées de WotLK. Les résolveurs de spawns (CreatureIdHelper,
branches objet et objet-ramassé de GetResultingWps) ne sautent plus les zones
sans carte ; ils répondent comme le détour triggerEnd de la v43.2 : convertir
les coordonnées de l'entrée en coordonnées du monde, prendre le point de
passage de route le plus proche. Mémoïsé par génération du cache de points de
passage ; le filtre de faction lit UnitFactionGroup.
77 quêtes à objectif de terrain ont désormais une vraie cible
Simple : 77 quêtes dont l'objectif est un lieu ou un objet sur le terrain (les
puits de Mulgore, la pierre de sélénien des druides, les pylônes de cristal
du cratère d'Un'Goro, la dépouille de Rolf, les quatre totems draeneï, la
série Dressage de bêtes des chasseurs et bien d'autres) n'avaient aucune
donnée de cible - l'entrée « Cible de quête » manquait tout simplement.
Toutes ont désormais une vraie cible routable. Important : les objets et
créatures viennent des données d'apparition propres de Sku, mais 26 lieux
proviennent de sources web (warcraft.wiki.gg, Wowhead) et n'ont pas tous pu
être vérifiés en jeu. Ils ont été choisis avec soin mais peuvent être
inexacts dans certains cas - un retour indiquant si une telle cible est
juste ou non aide beaucoup. Même un point imprécis vaut mieux que rien : la
route mène dans la bonne région.
Technique : Résultat de la liste de relecture
dev/rework-docs/QUEST-TARGET-PROPOSALS.txt (sections 1-3, relue à la main) :
27 cibles objet et 24 cibles créature comme entrées objectives dans
quests_fixes.lua (identifiants validés contre les bases propres, zones
d'apparition vérifiées, aucune position -1,-1 en extérieur) plus 26
coordonnées triggerEnd issues de la recherche web. Deux quêtes (9410, 10166)
portaient déjà le bon identifiant d'objet dans extraObjectives, que les
menus de cible ne lisent pas - le champ objectives y a été ajouté.
Treize quêtes de dialogue ont retrouvé leur point de remise
Simple : Treize quêtes - surtout les anciennes quêtes de sorts de prêtre (Étoiles
d'Élune, Grâce d'Élune, Sans peur, Maléfice de faiblesse), plus entre autres
« Le tome de noblesse » et « Allié des Aile-du-Néant » - n'avaient aucun point
de remise dans les données : ni cible ni livraison, rien n'était routable.
L'entrée Livraison mène désormais au PNJ que le texte de la quête nomme
lui-même.
Technique : Entrées finishedBy dans quests_fixes.lua ; les identifiants de
créature ont été résolus depuis le texte de quête contre la base de données des
créatures (chaque nom y est unique, et les points d'apparition se trouvent dans
la zone que le texte nomme). Les candidats proviennent du nouvel outil
dev/rework-docs/_quest_target_candidates.py, qui classe toutes les quêtes sans
cible et écrit la liste de relecture QUEST-TARGET-CANDIDATES.txt pour les
restantes (vrais objectifs de terrain sans source de coordonnées).
Expérimental : itinéraires de récolte
Simplement : Vous choisissez une ressource - un minerai, une herbe, un nuage de gaz
ou une sorte de coffre - et Sku vous guide à travers la zone, gisement après
gisement, toujours le plus proche d'abord. Là où les données de chemin couvrent
le gisement, il emprunte un véritable itinéraire ; sinon vous obtenez une
balise directe et Sku vous le dit - une balise peut pointer à travers un mur,
un itinéraire non. En vol, il n'y a jamais d'itinéraire : là-haut la ligne
droite est la bonne réponse, pas le pis-aller.
Au gisement, Sku attend brièvement pendant que vous récoltez, puis poursuit. Le
raccourci « point de passage suivant de l'itinéraire » (Ctrl+Maj+W par défaut)
ignore le gisement courant pendant un itinéraire de récolte ; aucune nouvelle
touche n'est donc nécessaire.
Si le pistage correspondant est actif (Détection des minerais, Détection des
herbes), l'analyseur de minicarte vérifie à l'approche si le gisement est
encore là, et ignore ceux déjà récoltés avec une annonce propre. Sans pistage
l'itinéraire fonctionne quand même, simplement sans cette vérification.
À trouver sous Réglages -> Scan -> analyse des ressources -> Itinéraires de
récolte, avec le rayon de vérification, le nombre de gisements par itinéraire
et l'interrupteur de la vérification. Pour les minerais, les herbes et les
coffres, « Afficher les points de passage d'herbes et de filons » doit être
activé dans les réglages de navigation - sinon ces gisements n'existent pas du
tout en tant que points de passage, et Sku le dit précisément au démarrage au
lieu de déclarer la zone vide à tort. Les nuages de gaz n'ont pas besoin de ce
réglage.
Important : Cette fonction est expressément expérimentale. Elle n'a été essayée en
jeu que dans les grandes lignes, et la vérification « le gisement est-il encore
là » n'a pas pu être exercée dans des conditions réelles, car aucun personnage
ici n'a de métier de récolte - sans Détection des minerais ni Détection des
herbes, il n'y a aucun point sur la minicarte que l'analyseur puisse lire. Si
vous jouez Minage ou Herboristerie : essayez et signalez ce qui ne va pas.
Technique : L'idée et l'implémentation de référence viennent de ZenqFR, de l'addon
compagnon SkuGatherRoute (github.com/ZenqFR/Sku-GatherRoute) ; la mécanique
d'itinéraire y a été conçue et démontrée - une famille de points de passage
sous un nom de base commun, chaque gisement atteint par un itinéraire proche,
une annonce distincte pour chaque transition, et le raccourci existant comme
touche pour ignorer. Ici la fonction est reconstruite nativement avec l'accord
de ZenqFR, afin qu'aucun addon supplémentaire ne soit nécessaire.
Une différence est délibérée : l'original nécessite GatherMate2, cette version
non. Nous n'y voyons aucun avantage - les gisements se trouvent depuis
longtemps dans le cache de points de passage de SkuNav, coordonnées mondiales
comprises, et nommés de telle sorte que la ressource EST le nom de base du
point de passage ; une deuxième source de données et un deuxième addon
n'y ajouteraient rien tout en coûtant une installation de plus à chaque
utilisateur. Si GatherMate2 fait quelque chose qui nous a échappé -
l'enregistrement automatique des gisements réellement récoltés serait le
candidat -, dites-nous alors à quoi cela sert, et nous y regarderons.
La vérification de présence s'accroche à MinimapScanner:MinimapScanFastStop, le
point unique par lequel passe chaque analyse. Si « notifier les ressources »
est activé, l'analyse déjà en cours suffit ; sinon l'itinéraire analyse
lui-même, de manière limitée et uniquement à portée de la cible, et ces
analyses restent silencieuses. Une correspondance confirme, un échec ne prouve
rien - une analyse ne rapporte jamais qu'un seul nom et peut avoir attrapé un
autre point. Ce n'est qu'après plusieurs analyses infructueuses à portée qu'un
gisement est considéré comme disparu. Dans le doute, rien n'est jamais ignoré :
un gisement conservé à tort coûte quelques pas, un gisement ignoré à tort ne se
remarque pas du tout.
Le menu reste utilisable pendant la marche
Simple : Si vous ouvriez le menu en marchant, il s'affichait et annonçait « menu
ouvert », mais plus aucune touche ne faisait quoi que ce soit - ni flèche, ni
lettre, ni Entrée. Seule Échap passait. Il ne redevenait utilisable qu'une fois
à l'arrêt. C'est corrigé : un menu ouvert accepte désormais les touches que
vous marchiez, couriez ou soyez en course automatique.
Technique : Il y avait deux verrous de mouvement, pas un. Le premier empêche
l'OUVERTURE tant qu'une touche de déplacement est maintenue - il reste, et il
est nécessaire : les raccourcis du menu avaleraient le relâchement de la touche
et vous courriez sans fin. La course automatique en est exemptée depuis la
v42.00 (son propre indicateur AutoRun, que IsPlayerMoving ne lit délibérément
pas). Le second se trouvait dans le gestionnaire de touches du menu lui-même
et, tant qu'un verrou de mouvement était posé, laissait expirer chaque appui
sauf Échap. Ce second verrou n'apporte rien : une fois le menu ouvert, les
raccourcis lui appartiennent déjà, écarter l'appui n'arrête aucun mouvement -
il ne fait que rendre le menu sourd, et marque de surcroît « ouvrir plus tard »
sur un menu déjà ouvert, que la boucle de réouverture doit ensuite défaire. Il
ne s'applique plus que si le menu n'est PAS ouvert. Les quatre verrous
d'ouverture restants écrivent en outre une ligne dans le journal de débogage
nommant la touche de déplacement qui les retient - auparavant ils étaient
muets, et un rapport « le menu ne s'ouvrait pas en marchant » ne laissait
aucune trace.
Navigation : « Carte actuelle par distance » vient désormais en premier
Simple : Sous « Point de passage direct », « Récent » était en première position et
« Carte actuelle par distance » en deuxième. Les deux sont permutés - la liste
des points de passage de la zone actuelle est l'entrée la plus fréquente, et
elle se trouve maintenant directement sous le curseur.
Technique : Uniquement l'ordre des injections dans SkuNav:MenuBuilder. « Récent »
reste conditionné à une liste non vide de points de passage récents ; si elle
est vide, « Carte actuelle par distance » est de toute façon en tête, comme
avant.
Le filtre de saisie ignore les accents
Simple : Dans chaque liste que l'on peut restreindre en tapant, les lettres
accentuées étaient traitées comme des caractères d'une autre langue.
« general » ne trouvait rien alors que « Fournitures générales » était là, et
« ingenieur » ne trouvait pas « Ingénieur » - alors que « ing » y parvenait.
C'est maintenant la lettre qui compte, pas l'accent. De plus, la touche ù d'un
clavier AZERTY alimente désormais aussi le filtre : c'est la seule lettre
accentuée que ce clavier livre comme véritable événement de touche.
Technique : string.lower en Lua travaille octet par octet et ne transpose que A-Z ;
il ne replie donc jamais un caractère accentué, d'aucun côté de la comparaison.
SkuMenuFilterMatch (utilisé par ApplyFilter et JumpToFilterMatch) et
SettingsSearchMatch replient désormais le nom de l'entrée et le texte tapé en
ASCII sans accents avant de comparer ; seules les séquences de 2 octets du
supplément Latin-1 et du latin étendu A sont touchées, l'ASCII pur emprunte une
voie rapide. Strictement plus permissif qu'avant : ce qui correspondait
correspond toujours. Les deux appels string.find passent en outre plain=true -
le texte tapé était lu comme un motif Lua, si bien qu'une saisie comme « 50% »
ou « (long » ne trouvait rien ou levait une erreur.
Le repli des accents vient de la PR #9 (ZenqFR), la touche ù de la PR #13
(Naxedim).
Barres d'action : l'infobulle du sort est de nouveau lue
Simple : Dans le menu des barres d'action (Maj+F11), Maj+Bas sur un sort de la
liste d'affectation ne lisait plus rien du tout. L'infobulle est de retour - de
même que pour le contenu actuel d'une touche et pour les deux lectures de
capacités du familier voisines, qui présentaient la même faiblesse.
Technique : Deux causes indépendantes, toutes deux traitées car aucune ne pouvait
être écartée à la seule lecture du code. D'abord, SkuScanningTooltip est créé
une fois à la connexion, sans modèle, et partagé par tous les modules ; celui
qui l'a utilisé en dernier peut le laisser sans propriétaire exploitable, après
quoi chaque appel Set* ne remplit plus rien - en silence, sans erreur. Il est
désormais réapproprié juste avant d'être rempli. Ensuite, SetSpellByID recevait
la troisième valeur de retour de GetSpellBookItemName, qui suit sur ce client
la signature Classic et ne renvoie que (name, rank) - l'identifiant est un
ajout retail et valait nil. L'identifiant provient maintenant de
GetSpellBookItemInfo, et l'infobulle est remplie par SetSpellBookItem, qui n'a
besoin d'aucun identifiant. La lecture est en outre encapsulée dans un pcall,
puisqu'elle se déclenche à chaque déplacement du curseur. Issu de la PR #10
(ZenqFR).
Objectif de quête : le lieu figurait dans les données, mais restait introuvable
Simple : 174 quêtes qui demandent d'explorer un endroit ou d'y utiliser quelque
chose ne répondaient que « Vide » sous « Objectif de quête ». L'entrée mène
désormais au point de passage de route le plus proche de ce lieu.
Technique : GetTriggerEndWps composait un NOM de point de passage
(« ;;;; ») et le résolvait via la recherche par
nom exact dans le cache des points de passage. Or rien ne génère de point de
passage sous ce nom - contrairement aux apparitions de créatures et d'objets -
il ne trouvait donc quelque chose que si quelqu'un en avait écrit un à la main
dans les données de route : il en existe exactement un (quête 10211,
Shattrath). Le nom composé reste utilisé lorsqu'il se résout ; sinon la
géométrie prend le relais - coordonnées de carte vers coordonnées monde, puis
le point de passage de ROUTE réel le plus proche. GetNearestWpToCoords2 a reçu
pour cela un filtre typeId optionnel, afin que le repli n'atterrisse pas parmi
les quelque 145 000 points d'apparition générés, plus une protection contre un
seau de continent manquant, où pairs(nil) levait une erreur. Toutes les paires
de coordonnées de chaque zone sont désormais lues au lieu de la première seule,
et le résultat est mémorisé par génération du cache.
Français : les commentaires de points de passage reparlent - et en français
Simple : Les commentaires attachés aux points de passage - dont ceux qui touchent à
la sécurité, comme « attention, à partir d'ici la route longe le bord d'un
ravin », « attendre le zeppelin ici » ou « ce PNJ se déplace » - étaient
totalement muets sur un client français depuis la v42.11. Ils reparlent, et ils
parlent désormais français : 207 textes répartis sur 546 points de passage.
Technique : Les données de route ne portent lComments que pour enUS et deDE
(environ 296 points de passage). SkuNav:PlayWpComments lisait
comments[Sku.Loc] sans repli. Jusqu'à la v42.11, Sku.Loc valait « enUS » sur un
client français faute de locale frFR, et l'on entendait les textes anglais ;
depuis que locales/frFR.lua est livré, Sku.Loc vaut « frFR », la recherche
renvoie nil et la fonction sort sans un mot. Nouveauté : Sku.locList
(SkuUtil.lua), le pendant liste de Sku.locStr, renvoie la première liste NON
VIDE parmi Sku.Loc / enUS / deDE. « Non vide » est déterminant, car SkuMM.lua
matérialise un comments[Sku.Loc] vide sur chaque point de passage qu'il dessine
- une simple chaîne de « ou » s'y accrocherait. La traduction est faite depuis
l'original ALLEMAND, non via l'anglais : l'anglais avait perdu du détail sur
précisément ces textes de sécurité, et plusieurs entrées anglaises sont vides,
dont l'avertissement sur les ascenseurs mortels qui figure sur huit points de
passage.
Français : noms de ressources et la Caverne des Brumes
Simple : Deux défauts qui ne touchaient qu'un client français. D'abord, 22 des 94
noms de ressources du scanner de minicarte étaient erronés - le scanner les
compare à l'infobulle du jeu, un nom faux signifie donc que ce gisement n'est
jamais annoncé, sans message d'erreur. Ensuite, dans la Caverne des Brumes la
liste des points de passage restait vide, alors qu'une route lancée à
l'extérieur continuait de fonctionner.
Technique : Chacun des 22 noms a été tranché à l'aide d'une source indépendante des
deux addons : 19 par les tables frFR de SkuDB (importées des chaînes Blizzard
de Questie), trois par l'API d'infobulle de Wowhead. Pour la caverne, Geo.lua
compare GetMinimapZoneText() mot pour mot à L["Caverne des Brumes"] ; la locale
frFR générée portait « Grotte des Brumes », une traduction libre - le client
dit « Caverne des Brumes ». La comparaison était donc morte, uiMap restait sur
le continent, GetAreaIdFromUiMapId répondait -1 et GetCurrentAreaId nil, si
bien que ListWaypoints2 abandonnait. La valeur a été relevée avec /szp dans la
zone, non traduite, et figure désormais tant dans le fichier généré que dans le
magasin TSV à partir duquel il est assemblé - avec la note interdisant de la
traduire librement. Issu des PR #14 et #16 (Naxedim).
Tourner vers la balise : plus précis, et avec anticipation
Simple : La rotation automatique vers la balise (T) tombe désormais bien plus
juste. Auparavant elle allait si vite que le seul arrêt à la frontière d'une
image pouvait coûter vingt degrés de dépassement ou plus, selon la fréquence
d'images. La vitesse suit maintenant l'ampleur de la rotation, et aucune
rotation ne dure plus d'un quart de seconde. En mouvement, elle anticipe
également : elle vise là où se trouvera le point de passage à la fin de la
rotation - ce qui met fin aux tours en rond autour des points de passage
proches, surtout monté et en vol. Et un appui pendant une rotation en cours est
écarté au lieu d'interrompre celle-ci en plein milieu ; les appuis répétés
habituels affinent toujours comme avant.
Technique : Quatre éléments, testés ensemble. Le calage sur la caméra par défaut
avant la rotation reste - il porte le lacet, sans lui l'angle est faux. La
vitesse de rotation est plafonnée d'après l'angle restant au lieu de tourner à
plein régime. L'anticipation calcule, à partir de la vitesse actuelle et de la
durée prévue de la rotation, où sera le point de passage à la fin. S'y ajoute
une protection contre le défaut par lequel, après des rotations déclenchées
coup sur coup, la vitesse de rotation des touches de caméra pouvait rester
quadruplée définitivement.
Le programme d'installation retient où se trouve votre WoW
Simple : Le programme d'installation cherche le jeu aux endroits habituels -
Program Files, plus une poignée de dossiers probables sur C, D et E. Qui a son
WoW ailleurs devait chercher le dossier à la main, et pas une fois mais à
CHAQUE mise à jour : le programme oubliait la réponse au moment même où il se
fermait. Il note maintenant le dossier dès qu'une installation a abouti, et
recommence là la fois suivante. Lui montrer un client permet aussi de trouver
l'autre à côté : une Classic Era voisine d'une Anniversary sur le même disque
inhabituel est prise en compte toute seule. Si le jeu déménage plus tard ou est
désinstallé, la note est abandonnée et la recherche se déroule comme avant. Le
programme d'installation passe ainsi en version 4.5.
Technique : Nouveau fichier PathMemory.cs. Le manifeste d'installation ne pouvait
pas servir à cela - SkuInstall.json se trouve DANS le dossier AddOns, le lire
suppose donc d'avoir déjà trouvé le dossier que l'on veut retenir. Le dossier de
chaque client est donc écrit sous forme d'une simple ligne « produit=chemin »
dans SkuPaths.txt, à deux endroits : %LOCALAPPDATA%\SkuUpdater, à côté de la
copie permanente de l'updater que lance le raccourci « Sku Updater », et
%ProgramData%\SkuUpdater comme miroir valable pour toute la machine. Ce miroir
n'est pas une redondance gratuite : le programme demande l'élévation, un
utilisateur standard qui répond à l'invite UAC avec les identifiants
administrateur de quelqu'un d'autre exécute donc tout sous cet autre profil et
écrirait sinon la note dans un LOCALAPPDATA qu'il ne reverra jamais. Au
démarrage suivant, un dossier retenu l'emporte sur la détection - c'est une
décision, alors que la détection n'est qu'une supposition sur une courte liste
de disques - et son répertoire de base est placé en tête de la liste sondée,
ce qui est précisément ce qui fait que retenir un client profite à l'autre.
Garde-fous contre une note périmée : le dossier doit exister encore, et son
.flavor.info ne doit pas annoncer entre-temps un autre client ; sinon la note
est ignorée plutôt que d'envoyer une installation dans le vide. L'écriture n'a
lieu qu'après l'installation propre d'un client : une exécution annulée ou
échouée ne laisse donc rien. Du texte brut à dessein - le fichier se laisse lire
à voix haute et corriger dans le Bloc-notes, et supprimer une ligne rend la
détection automatique à ce client. Nouveau contrôle sans interface, « SkuSelfTest
pathmemory » : il affiche ce qui est noté sur cette machine et déroule
l'écriture, la lecture et les cas de note périmée sur un stockage jetable, sans
jamais toucher au vrai.
La fenêtre de lecture devient configurable
Simple : Les textes de quête, les infobulles, l'historique de discussion et les pages du
wiki aboutissent tous dans la même fenêtre de lecture. Son apparence était figée dans
le code : Playfair Display en 12 points, une police à empattements fine, dans la plus
petite taille utilisée par l'addon. Pour une personne ayant une vision résiduelle,
c'était à la fois la surface la plus importante de l'addon et la moins lisible.
Réglages -> Aides visuelles -> Fenêtre de texte propose désormais des tailles de 14 à
44 points, quatre polices, quatre combinaisons de couleurs (blanc sur noir, jaune sur
noir, noir sur blanc, blanc sur bleu), l'opacité du fond et un contour. Par défaut la
fenêtre s'affiche maintenant sans empattement en 18 points : même sans rien changer,
elle est nettement plus lisible qu'avant.
Technique : La surface appartient à Libs/SkuTTS-1.0 (cadre SkuTTSMainFrame et son
FontString .FS), où l'apparence tenait en un seul appel SetFont codé en dur. Les
réglages vivent désormais dans SkuCore VisualAids sous visualAids.textWindow, et
SkuTTS:Output appelle VisualAids:TextWindowLayout avant chaque affichage - protégé par
pcall et dans le même sens que SkuZOptions alimente déjà la barre de lecture. La
bibliothèque n'a ainsi rien à savoir des options, et un module Aides visuelles
désactivé laisse simplement la fenêtre telle quelle. Chaque attribution de police est
vérifiée et retombe sur la police standard du client au lieu de laisser une surface
vide - les deux familles fournies, Raleway et Playfair, se trouvent sous
Libs/SkuTTS-1.0/fonts et ne sont livrées que par l'archive de version. Les paliers de
taille sont volontairement plus petits que ceux de la barre : un texte de quête entier
tient ici, et la fenêtre n'a pas d'ascenseur, donc elle coupe d'autant plus tôt que la
police est grande. Tout reste lu intégralement ; l'affichage est la seconde piste.
La barre de lecture affiche ce que Sku dit - et fonctionne en combat
Simple : Deux choses à la fois. D'abord, la barre restait vide en combat, c'est-à-dire
précisément quand suivre le texte compte le plus. Ensuite, elle n'affichait que le nom
d'une entrée de menu : sur un interrupteur on avait le nom mais pas l'état, sur une
ligne de sac le nom mais pas la quantité - tout ce que Sku lit en plus manquait sur la
barre. Les deux sont corrigés ; la barre affiche maintenant la même ligne que Sku
prononce, y compris en plein combat. Sa police et ses couleurs sont en outre
réglables, avec des valeurs propres à côté de celles de la fenêtre de texte.
Technique : Le blocage en combat venait d'une confusion entre visible et ouvert : le test
s'appuyait sur SkuOptions:IsMenuOpen(), qui interroge OnSkuOptionsMain:IsVisible(). Ce
cadre est un ancêtre des boutons ENTER sécurisés, donc protégé, et son Show() est
bloqué en combat - le menu de combat tourne volontairement sans affichage et se
signale via SkuOptions.combatMenuActive. templates.lua et SkuZOptions testent déjà
exactement ce couple ; la barre de lecture ne le connaissait pas. Elle-même est un
cadre ordinaire sans enfant sécurisé : son Show() n'a jamais posé problème en combat.
Comme le chemin de combat ne ferme le menu que logiquement (le Hide du cadre y est
protégé) et le fait à six endroits, la barre se masque désormais d'elle-même : un
OnUpdate vérifie toutes les 0,25 s si le menu est encore ouvert - WoW n'appelle
OnUpdate que sur les cadres visibles, cela ne coûte donc rien une fois masquée. Le
contenu, lui, était un paramètre jeté : l'appelant transmettait depuis toujours la
chaîne parlée complète, mais c'est currentMenuPosition.name qui était affiché. La
chaîne transmise est désormais prioritaire, et les points-virgules du séparateur de
parole deviennent des tirets à l'affichage.
Barres de nom : les réglages que le jeu cache
Simple : Le client sait depuis longtemps afficher ses barres de nom plus grandes, plus
colorées et plus contrastées - simplement, il ne le propose nulle part. La fenêtre
d'options de Blizzard n'affiche aucun de ces réglages sous Classic ; la case pour des
barres de nom plus grandes qui existe dans le WoW actuel est explicitement laissée
vide sous Classic. Sans Sku, on n'y accédait que par des commandes console. Le nouveau
menu sous Réglages -> Aides visuelles -> Barres de nom les rend accessibles : taille
des barres, mise en évidence de la cible, atténuation de toutes les autres, couleurs
de classe pour les ennemis et les alliés, et noms seuls pour les joueurs alliés.
Contrairement à l'ancienne coloration, cela ne peint pas quelque chose derrière la
barre mais modifie la vraie barre. Par défaut, tout reste tel que le client le livre :
si vous ne choisissez rien, rien ne change.
Technique : Uniquement des réglages de CVar, aucun cadre propre, aucune contamination. La
taille écrit nameplateMinScale et nameplateMaxScale ensemble (les séparer produirait
une mise à l'échelle dépendante de la distance, dont personne ne veut ici), la mise en
évidence écrit nameplateSelectedScale, l'atténuation nameplateMinAlpha, auxquels
s'ajoutent nameplateShowClassColor, nameplateShowFriendlyClassColor et
nameplateShowOnlyNameForFriendlyPlayerUnits. Toutes les valeurs et l'existence de
chaque CVar ont été mesurées sur le client vivant 2.5.6.69546, et le plafond de 2.0
vérifié contre l'écrêtage du client. Important pour la suite : le dossier
BlizzardInterfaceCode extrait dans le répertoire du client date de mars/avril 2026 et
est donc antérieur au changement des barres de nom de juillet ; il cite des noms de
CVar de la version actuelle comme NamePlateVerticalScale ou ShowClassColorInNameplate
qui n'existent pas du tout dans cet exécutable. Ce sont les chaînes de l'exécutable
qui font foi. Les écritures de CVar sont bloquées en combat et échouent en silence :
une modification faite là est donc mémorisée et appliquée à PLAYER_REGEN_ENABLED. Sku
réapplique les valeurs à la connexion, mais seulement une fois qu'un choix a
réellement été fait dans le menu - qui a réglé ses barres de nom à la main par la
console les conserve intactes.
La coloration des barres de nom survit à un redémarrage
Simple : Qui activait la coloration des barres de nom l'avait exactement jusqu'à sa
prochaine connexion ou au prochain rechargement de l'interface. Ensuite l'interrupteur
indiquait toujours « activé », mais plus rien n'était coloré. Le seul moyen de la
relancer était de basculer l'interrupteur à la main, à chaque session. C'est corrigé ;
le réglage fonctionne désormais simplement.
Technique : L'armement du pilote d'événements (NAME_PLATE_UNIT_ADDED, _REMOVED et
PLAYER_TARGET_CHANGED) ne tenait qu'au OnAction de l'interrupteur du menu. La valeur
enregistrée était bien lue au chargement mais appliquée nulle part : après chaque
connexion, aucun événement n'était donc enregistré. L'armement se trouve maintenant
dans VisualAids:OnEnable, là où l'avertissement de suivi et le bouton du prochain
ennemi ont toujours été armés, avec une seconde tentative différée au cas où le profil
ne serait pas encore prêt au premier passage.
Une fenêtre fermée n'est plus annoncée après coup
Simple : Apprendre plusieurs sorts chez un maître, fermer la fenêtre rapidement puis
ouvrir sa fenêtre de métier ramenait la fenêtre du maître au bout d'un moment -
parfois seulement après la lecture d'une infobulle. Sku retenait où il comptait encore
sauter dans le menu et le faisait plus tard, sans vérifier si cette fenêtre existait
toujours.
Technique : Le saut de menu différé tient en trois champs (openMenuAfterCombat,
openMenuAfterMoving, openMenuAfterPath) qui étaient jusqu'ici écrits indépendamment.
SlashFunc arme le drapeau ET le chemin ensemble ; d'autres endroits n'armaient que le
drapeau - et héritaient donc du chemin qui traînait encore. Le chemin n'était
d'ailleurs effacé que dans la partie différée de CheckFrames, qui abandonne tant qu'on
se déplace et ne reprend qu'une demi-seconde plus tard, alors que la libération
s'exécute à chaque image et se déclenche à l'instant où l'on s'arrête - une course que
la libération gagnait. Les trois champs forment désormais un tout
(SkuCore:ClearDeferredMenuOpen), armer un drapeau efface le chemin, et l'effacement a
lieu dans le hook Hide de la fenêtre (SkuCore:GENERIC_OnClose) - exactement au moment
où le saut devient caduc. Délibérément sans délai d'expiration ni contrôle de
visibilité : l'état est effacé là où il devient faux, au lieu d'être distancé.
Le menu n'ouvre plus son niveau le plus haut tout seul
Simple : De temps en temps le menu s'ouvrait de lui-même - et tout en haut, au lieu de
là où l'on voulait aller.
Technique : SkuCoreControlOption1 porte des raccourcis clavier (distance de la cible, mode
panique, scan de la minicarte, annuler le vol, se tourner vers une unité) et n'a rien
à voir avec le menu. Son OnShow abandonne tant qu'on se déplace ou qu'on est en
combat, car SetOverrideBindingClick y est interdit et avalerait le relâchement d'une
touche de déplacement maintenue - mais il abandonnait en posant openMenuAfterMoving,
et la libération, faute de chemin, en faisait une ouverture du menu à sa racine. Ce
cadre a maintenant son propre drapeau (rebindControlKeysAfterBlock) et ne récupère
que ses touches.
La fermeture d'une seconde fenêtre ouverte est de nouveau remarquée
Simple : Avec deux fenêtres ouvertes en même temps - un marchand et les sacs, par exemple
- seule la première fermeture était remarquée. À la seconde, le menu ne se
reconstruisait pas et ne se fermait pas.
Technique : L'un des deux installateurs de hooks enveloppait Show et Hide autour d'un
unique interrupteur partagé (SkuDropdownlistGenericFlag) : tout Show le posait, et
seul le premier Hide suivant le consommait. Quel installateur prenait une fenêtre
était une pure question de temps - celui-ci prend tout ce qui existe une seconde
après l'entrée dans le monde, tandis que des fenêtres comme le maître, le métier et
l'hôtel des ventes naissent plus tard et reviennent à l'autre. L'interrupteur a
disparu ; GENERIC_OnClose regroupe de toute façon lui-même.
Plus de contenu d'une fenêtre fermée sous « Local »
Simple : Après la fermeture d'une fenêtre, ses entrées pouvaient rester sous « Local »
et réapparaître à la prochaine ouverture du menu.
Technique : SkuCore.GossipList est la source de données du menu Local, mais elle n'était
vidée que si le menu était ouvert à cet instant précis. Sur le chemin d'Échap il ne
l'est jamais : le cadre du menu descend volontairement avant les fenêtres, et la
partie de code concernée s'exécute une image plus tard. Elle est maintenant vidée dès
qu'aucune fenêtre n'est ouverte.
Maître : le silence une fois la fenêtre fermée
Simple : Apprendre plusieurs sorts coup sur coup et fermer la fenêtre juste après
laissait les annonces du maître continuer.
Technique : Chaque clic sur « Apprendre » mettait en file une re-lecture après une
demi-seconde et une annonce après 0,85 seconde, sans possibilité d'annulation. Les
deux étapes vérifient maintenant que ClassTrainerFrame est encore visible. Le
garde-fou de la v43.1, qui évite de parler dans un menu fermé, n'aidait pas ici : dès
que la fenêtre suivante rouvrait le menu, il était ouvert. De plus, la reprise des
re-lectures silencieuses en déplacement perdait sa consigne de silence et était
planifiée une fois par clic - elle transporte désormais ses arguments et ne s'exécute
qu'une fois.
Hôtel des ventes : la liste des résultats est vraiment triée
Simple : Trier par « Niveau décroissant » donnait malgré tout une liste qui commençait par
du bas niveau. Le tri n'agissait qu'à l'intérieur d'une page - et une page, ce sont les
50 enchères les moins chères. En haut se trouvait donc le plus haut niveau parmi la
camelote grise la moins chère, tandis que les objets de niveau 70 étaient des centaines
d'entrées plus bas. Même après le son de fin, car rien n'était jamais retrié.
Maintenant, c'est le serveur qui trie lui-même la liste par niveau ; à niveau égal,
c'est le prix à l'unité qui décide.
Technique : Chaque requête partait avec le tri serveur figé sur « unitprice », et la liste
des résultats se construit page après page, par simple ajout à la fin (les nouveaux
objets vont en bas pour que le curseur ne saute pas). L'ordre de la liste est donc
toujours celui du serveur, et notre propre tri n'ordonnait qu'une page. Pour « Niveau
croissant » et « décroissant », la réponse du serveur est désormais triée sur la
colonne « level » - celle-là même que règle le bouton de tri par niveau de Blizzard.
L'achat et l'achat stratégique restent sur « unitprice » : ils ont besoin de l'offre la
moins chère en page 0. Une recherche par nom aussi : tous les résultats y ont le même
niveau, le serveur aurait trié sur un champ où rien ne diffère et aurait renvoyé ses
pages dans son propre ordre au lieu des moins chères d'abord. De plus, les
comparateurs de niveau utilisent maintenant le prix à l'unité comme second critère ;
les enchères sans achat immédiat comptent avec leur enchère à l'unité au lieu d'un
prix d'achat de 0.
Hôtel des ventes : « niveau » signifie partout le niveau requis
Simple : Le niveau affiché dans les résultats était tantôt le niveau requis, tantôt le
niveau d'objet, selon la liste d'où il venait et selon que l'objet a ou non une
exigence. Une pile d'Étoffe du Néant (exigence 0, niveau d'objet 55) passait ainsi
devant une épée de niveau 40. C'est désormais toujours le niveau requis, la colonne que
l'hôtel des ventes lui-même appelle « niveau ». Et il n'est plus lu que là où il
distingue les entrées : dans une catégorie ou dans la liste du scan complet. Une
recherche par nom d'objet ne renvoie que des enchères du même objet - le même nombre y
revenait 800 fois. Qui filtre par niveau continue de l'entendre partout.
Technique : Un lecteur commun aux deux listes de résultats : le champ 6 de la réponse du
serveur, sinon la valeur de SkuDB. Le champ 7 est pris en compte - pour les recettes,
le champ 6 contient la compétence de métier requise, pour les contenants le nombre
d'emplacements, et ce ne sont pas des niveaux. Le niveau d'objet n'est plus jamais
utilisé en remplacement. 0 signifie maintenant « aucune exigence de niveau » et non
« inconnu », d'où la disparition du « Niveau 0 » annoncé (0 est vrai en Lua, c'est
ainsi qu'il finissait par être lu).
Hôtel des ventes : « utilisables uniquement » ne cache plus rien
Simple : Dans la liste du scan complet, ce filtre ne laissait plus, dans certaines
catégories, que du bas niveau - précisément les objets de haut niveau recherchés
avaient disparu. La recherche normale n'a jamais eu ce défaut.
Technique : Dans la liste en direct, c'est le serveur qui filtre. Le scan complet récupère
tout sans filtre et Sku décide lui-même - jusqu'ici strictement d'après le champ canUse
de la réponse du scan. Le client ne peut remplir ce champ que s'il disposait déjà des
données de l'objet ; lors d'un scan complet, il ne les a pas pour la majorité des
lignes. Ce sont exactement les lignes dont le nom, le niveau et la qualité doivent être
réparés - seul canUse n'avait aucune réparation. Disparaissait donc ce que le joueur
n'a jamais vu, c'est-à-dire surtout le haut niveau. Un « non utilisable » ne compte
plus que sur une ligne complète ; sinon, c'est le niveau requis de SkuDB qui décide. La
base ne connaît ni les restrictions de classe ni celles de race - dans le doute, mieux
vaut montrer un objet de trop que taire celui que l'on cherche.
Hôtel des ventes : sans filtre, rien n'est filtré
Simple : Même sans filtre réglé, la liste du scan complet écartait tout ce qui n'a pas
d'exigence de niveau - marchandises, composants, beaucoup de recettes - et en plus tout
le gris, alors que le menu de qualité affichait toujours « Médiocre », donc aucune
restriction.
Technique : Les valeurs par défaut des filtres non réglés étaient un 1 au lieu d'un 0, et
le niveau était lu brut dans la réponse du scan. Une ligne dont le client ne
connaissait pas le niveau pendant le scan revenait à 0 et tombait donc sous le niveau
minimum de 1 ; la réparation depuis la base ne se déclenchait que sur une valeur
absente, pas sur un 0. Les deux valeurs par défaut sont maintenant 0, le niveau vient
du lecteur commun, et la réparation à la lecture traite 0 comme « absent ». Le niveau
minimum de l'utilisateur employé en dernier recours a disparu : une ligne ainsi
marquée passait toujours son propre filtre et était en plus annoncée avec ce niveau.
Hôtel des ventes : les catégories sans sous-catégories affichent de nouveau leurs objets
Simple : Sous « Enchères par objet », certaines catégories ne contenaient rien d'autre que
« Tout » - les objets de quête, par exemple.
Technique : La liste d'objets comparait la sous-classe même là où il n'y en a aucune. Une
catégorie sans sous-catégories est construite sans sous-classe, et la comparaison est
alors fausse pour chaque objet. Le jumeau du scan complet portait cette protection
depuis toujours ; la liste d'objets l'a maintenant aussi.
Hôtel des ventes : un achat annulé reste annulé
Simple : Annulez un achat, faites autre chose, et le dialogue d'achat pouvait revenir de
nulle part - et la touche Entrée suivante, destinée à ouvrir le champ de recherche,
achetait. C'était au moins le bon objet, mais personne ne l'avait demandé.
Technique : L'annulation libérait les minuteurs et les raccourcis, mais pas la cible
d'achat. La fonction de requête déduit son mode exactement de là - il existe une cible
d'achat, donc c'est une requête d'achat -, et la requête suivante, pourtant sans aucun
rapport, partait dans le chemin d'achat : il retrouvait l'ancienne cible dans la liste
fraîche, construisait le dialogue et armait la touche Entrée. Dans le journal du
01/09/2026, l'annulation (18:45:40), la nouvelle recherche (18:46:12, aussitôt
« MATCH FOUND, showing popup ») et l'achat (18:46:16, « enchère acceptée ») ne sont
séparés que par quatre appuis de touche. La cible d'achat est désormais effacée aux
trois endroits où l'intention d'acheter prend fin : annulation du dialogue (y compris
par la sécurité de 30 secondes), démarrage d'une nouvelle recherche ou liste, et
fermeture de l'hôtel des ventes. Le chemin d'achat lui-même reste intact : il ne passe
pas par l'entrée de recherche, et la nouvelle tentative après une course perdue
conserve sa cible.
Hôtel des ventes : une recherche interrompue le dit
Simple : Quand une recherche s'interrompt parce que le serveur ne répond plus, la liste à
moitié remplie restait affichée et ressemblait à un résultat terminé. On entend
maintenant « Recherche interrompue, liste incomplète ».
Technique : Le chien de garde met fin à une recherche paginée après 60 secondes sans
réponse du serveur, ou après 180 secondes au total - en silence, jusqu'ici. Le son de
fin est le signal du « complet », mais son absence ne s'entend pas. L'annonce n'a lieu
que si l'utilisateur se trouve réellement dans cette liste de résultats ; une recherche
qui tourne en arrière-plan reste silencieuse.
-------------------------------------------------------------------------------------------------------
Changements dans Sku v43.1
Aperçu
Changements
L'achat de plusieurs exemplaires à l'hôtel des ventes continue désormais tant qu'il existe d'autres offres au même prix audible.
Chaque achat réussi est immédiatement confirmé par « Acheté, n sur m ».
Quand un prix est épuisé, Sku annonce « Plus aucune offre à ce prix » avec le bilan et reste dans la liste des résultats.
Nouveau réglage « Énoncer l'écho du clavier » (Réglages -> Général, activé par défaut) : la lecture caractère par caractère pendant la saisie dans les champs de texte peut être désactivée.
Hôtel des ventes : le scan complet se confirme une première fois au bout de 5 secondes, puis toutes les 10 secondes comme avant.
Menu des quêtes : l'entrée « Membres du groupe » se place désormais en troisième position au lieu du milieu, ce qui laisse « Quêtes actuelles » et « Base de données des quêtes » toujours à la même place.
Corrections
Hôtel des ventes : l'achat de plusieurs exemplaires s'interrompait à tort après presque chaque achat réussi avec « Enchère épuisée » et éjectait de la recherche.
Après la fermeture du menu, des annonces différées continuaient de parler (« Nouvelle lettre plus », « Sujet : », « Envoyé », « Tout ouvert » depuis une boîte aux lettres fermée depuis longtemps).
Hôtel des ventes : une réponse incomplète du serveur ne termine plus la recherche à tort par « vide » - une vraie recherche sans résultat répond toujours immédiatement.
Hôtel des ventes : une recherche qui ne peut pas démarrer indique désormais pourquoi (« impossible, analyse en cours » ou « Hôtel des ventes occupé ») au lieu d'annoncer « vide ».
Hôtel des ventes : une recherche sans résultat annonce désormais « aucun résultat » au lieu de « vide » - et le champ de recherche reste dans la liste, le curseur s'y pose directement.
Hôtel des ventes : un scan complet annulé en fermant la fenêtre bloquait ensuite toute recherche avec « impossible, analyse en cours », alors qu'aucun scan ne tournait plus.
Hôtel des ventes : le scan complet mourait parfois en silence juste après l'annonce de départ ; le verrou de 16 minutes était tout de même consommé.
Clients français : la liste des points de passage était vide dans plusieurs zones (correction de Naxedim).
Nouveau réglage : Énoncer l'écho du clavier
Simple : Dans les champs de saisie (rédaction d'une lettre, recherche, nom de profil...),
Sku lit chaque caractère tapé, ainsi que la touche retour arrière et les lectures du
curseur avec les flèches. Si cela vous dérange ou si vous l'entendez en double, vous
pouvez désormais le désactiver sous Réglages -> Général, « Énoncer l'écho du
clavier » (juste à côté de « Énoncer les numéros du menu » et « Annoncer les
sous-menus »). Par défaut, c'est ACTIVÉ : rien ne change si vous ne touchez à rien.
Les annonces d'état comme « Annulé » ou la confirmation du champ après ENTRÉE
restent toujours.
Plus d'annonces dans un menu fermé
Simple : Valider un champ de saisie avec ENTRÉE puis fermer aussitôt le menu produisait
quand même « Sujet : » - comme si l'écho du clavier traînait après
la fermeture. De même, « Nouvelle lettre plus », « Envoyé » ou « Tout ouvert »
parlaient encore depuis une boîte aux lettres fermée depuis longtemps. Désormais :
tout ce qui veut annoncer une entrée de menu vérifie d'abord que le menu est encore
ouvert - et les accusés de la boîte aux lettres vérifient que celle-ci est encore
ouverte. La ligne système du serveur « Courrier envoyé. » dans le chat n'est pas
concernée.
Technique : Les retardataires ne venaient jamais de la file vocale (la fermeture la vide
déjà via queuereset + StopSpeakingText) mais de producteurs qui n'enfilent
qu'APRÈS la fermeture : confirmations C_Timer (0,3 s après pièce jointe/saisie/
envoi, 0,5 s après « Tout ouvrir ») et événements serveur (MAIL_SEND_SUCCESS). Une
purge générale supplémentaire de la file à la fermeture d'une fenêtre ne les
attraperait donc pas, mais couperait en plein mot des annonces légitimes (chat,
combat, navigation). À la place : une garde centrale dans
SkuOptions:VocalizeCurrentMenuName (pas de menu ouvert, pas de menu de combat sans
affichage et pas de menu de ligne SkuChat -> silence ; le mode chaîne renvoie
toujours le texte), plus trois gardes
côté producteurs dans le courrier (MailboxOpenFlag pour « Envoyé », IsMenuOpen pour
la confirmation de champ, visibilité de MailFrame pour « Tout ouvert »).
MAIL_FAILED reste volontairement non filtré - un échec doit rester audible même
après la fermeture.
Hôtel des ventes : l'achat de plusieurs exemplaires s'interrompait à tort après presque chaque achat avec « Enchère épuisée »
Simple : En choisissant une entrée de résultats comme « 14 fois Sphère du Vide » pour en
acheter cinq, on entendait « Enchère épuisée » après presque chaque achat et on se
retrouvait hors de la recherche - alors que l'achat avait réussi et que l'argent
était correctement débité. Comme le succès lui-même n'était jamais annoncé, cela
ressemblait à une série d'échecs, alors que les objets étaient déjà dans le sac. La
cause : les vendeurs se sous-enchérissent de 1 cuivre. La liste des résultats
regroupe toutes les enchères dont le prix SONNE pareil - au-dessus de 1 or, la part
de cuivre n'est pas prononcée -, mais la continuation d'achat cherchait l'exemplaire
suivant au montant de cuivre exact, qui n'existait généralement plus une fois le
moins cher acheté. La continuation parcourt maintenant un tel groupe pièce par
pièce : l'offre suivante ne peut différer que par le cuivre, la partie inaudible du
prix. Une offre audiblement plus chère - que ce soit de 1 argent ou de 100 or - n'est
JAMAIS proposée automatiquement, et chaque exemplaire exige toujours sa propre
confirmation.
Technique : Prouvé dans le journal de débogage du 2026-08-25 : cinq achats de 52po99a81c
à 52po99a84c, chaque tentative achetait exactement un exemplaire puis s'interrompait.
La continuation (_ABReQueryBuy → match scan) comparait ID d'objet + taille de pile +
prix d'achat immédiat EXACT ; or la liste des résultats regroupe selon le libellé
formaté (SkuGetCoinText very-short supprime le cuivre au-dessus de 1 or), un groupe
couvre donc une tranche de 1 argent. Le scan retient maintenant l'offre achetable la
moins chère avec le même ID d'objet et la même taille de pile dans la même tranche
(floor(buyout/100) identique, seulement à partir de 10000 cuivre) et reprend ses
champs de prix (8/9/10/11) dans QueryBuyData ; failCount repart à 0 pour le nouveau
groupe de prix. Sous 1 or, le libellé prononce le cuivre - l'ancien comportement
exact y demeure. Les achats par enchère gardent la comparaison stricte sur 17 champs.
Hôtel des ventes : chaque achat est confirmé, la fin d'un groupe de prix annonce le bilan et reste dans la liste
Simple : Après chaque exemplaire réussi, Sku dit maintenant immédiatement « Acheté, n sur
m » - avant, le seul signal de succès était l'invite d'achat suivante. Quand toutes
les offres au même prix audible sont parties, on entend « Plus aucune offre à ce
prix » avec le bilan, par exemple « 2 sur 5 acheté », et le curseur reste sur
l'entrée de la liste des résultats au lieu de remonter de plusieurs niveaux vers
« Enchères » comme avant. De là, on peut naviguer directement vers l'entrée
suivante, plus chère, et racheter.
Technique : L'annonce de succès se trouve dans _ABContinueOrFinish, juste après le
« Enchère acceptée. » du serveur. Le chemin « épuisée » positionne d'abord
(AuctionStayOnResultsEntry ; si l'élagage échoue parce que l'enregistrement a déjà
été retiré après l'achat, un repli via AuctionResultsItemEntryFromCursor garde le
curseur sur l'entrée de l'objet) puis prononce le message 0,4 s plus tard sans
interruption - le même schéma que le « Terminé. Tous achetés » existant.
Hôtel des ventes : une réponse incomplète du serveur ne vaut plus « aucun résultat »
Simple : Quand le serveur répond de façon contradictoire - il annonce des résultats
mais ne livre aucune ligne -, Sku terminait aussitôt la recherche par « vide ».
Dans ce cas, il attend désormais la réponse complète et redemande la page. Une
recherche qui ne trouve réellement rien (faute de frappe dans le champ de
recherche, catégorie vide) répond toujours immédiatement - avec 0 résultat et
« aucun résultat ».
Technique : Dans AUCTION_ITEM_LIST_UPDATE_LIST, une page vide ne compte comme
prématurée que si le serveur se contredit : tBatch == 0 (aucune ligne sur cette
page) alors que tCount > 0 (nombre total de résultats supérieur à 0).
QueryListEmptyWaits s'incrémente alors, le scan reste en « waiting » et le ticker
redemande la même page ; après 10 réponses de ce type, le résultat est tout de
même accepté (sortie de secours). tCount == 0 est au contraire la réponse honnête
« aucun résultat » et doit passer sans délai : le chemin d'achat compte ici
CHAQUE réponse vide (QueryBuyEmptyWaits) parce qu'il cherche à retrouver une
enchère peut-être encore vivante - pour une recherche, la même règle serait
fausse. Dans le journal de débogage du 2026-08-26, le serveur répond à la
recherche « feuerpartiekel » par 0 sur 0 dans la même seconde : c'est la vraie
réponse, pas une réponse fantôme.
Hôtel des ventes : une recherche qui ne peut pas démarrer indique désormais pourquoi
Simple : Il y a des moments où l'hôtel des ventes n'accepte pas une recherche :
pendant qu'un scan complet tourne en arrière-plan, pendant qu'un achat est armé,
ou quand le serveur limite les requêtes. Jusqu'ici Sku ne s'en apercevait pas :
il construisait quand même la liste et annonçait « vide », comme si la recherche
n'avait rien trouvé. Au premier essai d'une session, cela ressemblait à une
catégorie vide ; plus tard, la liste affichait même les résultats de la
recherche PRÉCÉDENTE.
Sku indique maintenant la raison. Si un scan complet tourne, « impossible,
analyse en cours » figure dans la liste dès l'ouverture - le message que le menu
de vente donne depuis toujours dans cette situation. Attendre n'y a aucun sens : un
scan complet occupe l'hôtel des ventes pendant des minutes. Pour les obstacles
courts (achat armé, limitation), Sku reste sur « Attendre » et continue d'essayer
de lui-même ; si la recherche passe dans les 5 secondes, tout continue
normalement, sinon il annonce « Hôtel des ventes occupé, recherche non
démarrée ». Si un scan complet démarre pendant ces tentatives, Sku les interrompt
aussitôt et le signale. Flèche gauche puis droite relance un essai à tout moment.
Technique : AuctionHouseStartQuery renvoie false à trois endroits et, depuis cette
version, la RAISON en deuxième valeur (« secureBuy », « getAllRunning »,
« getAllCooldown », « queryThrew »). Les trois appelants de navigation - champ de
recherche, « Tous » et objet unique dans AuctionHouseBuildItemDBMenu - ignoraient
cette valeur de retour et posaient malgré tout QueryResultsHost, vidaient
children et reconstruisaient ; le constructeur de résultats trouvait state ==
« idle », sautait sa branche « Attendre » et tombait sur le test de liste vide.
Ils passent désormais par AuctionBrowseStart, qui décide selon la raison : les
raisons getAll sont signalées immédiatement, tout le reste est mis en file comme
nouvel essai (QueryStartPending). AuctionBrowseRetryTick s'accroche au ticker
OnUpdate existant de l'hôtel des ventes - volontairement piloté par les frames
plutôt qu'une chaîne C_Timer, car une chaîne interrompue n'atteint jamais sa
ligne de réarmement -, se place tout en tête avant ses retours anticipés (sans
quoi il gelait pendant toute l'ingestion getAll), tente au plus toutes les 0,25 s
et seulement si CanSendAuctionQuery, revérifie la raison, et se remet
explicitement en file après un échec, puisque StartQuery appelle ResetQuery en
interne et efface ainsi le marqueur même lorsqu'il renvoie ensuite false. Après
un succès, QueryResultsHost est reposé, car la nouvelle requête le remet à nil.
QueryStartFailed porte le TEXTE à afficher (AH_QueryBusy ou le message de scan du
menu de vente) et est testé AVANT la condition « Attendre » : le scan bloquant
maintient lui-même AuctionScan.state sur « waiting »/« paging », sinon
« Attendre » l'emporterait toujours et la raison resterait inaccessible. Le
chemin immédiat ne parle volontairement PAS de lui-même : le message est la seule
entrée de la liste et est lu à l'ouverture de toute façon, alors qu'une annonce
distincte serait coupée par le vocalize suivant et repartirait à chaque passage
(OnEnter s'exécute déjà à l'arrivée). ResetQuery efface les deux marqueurs.
Hôtel des ventes : « aucun résultat » au lieu de « vide », et le champ de recherche reste accessible
Simple : Une recherche sans résultat - une faute de frappe, par exemple - annonçait
« vide ». Cela évoque une liste vide, alors que le sens est « rien trouvé » :
elle annonce désormais « aucun résultat ». De plus, le champ de recherche
disparaissait de la liste après une recherche ; pour en lancer une seconde, il
fallait sortir puis revenir. Il reste maintenant dans la liste sous
« Suchbegriff eingeben », et en cas de zéro résultat le curseur s'y pose
directement - une pression sur ENTRÉE et l'on saisit le terme suivant. S'il y a
des résultats, le curseur se pose sur le premier comme auparavant.
Technique : La construction incrémentale appelait directement le constructeur de
résultats et contournait ainsi le BuildChildren de l'entrée de recherche - c'est
là qu'est inséré « Suchbegriff eingeben », d'où sa disparition après la première
page. Elle appelle maintenant aHost:BuildChildren(aHost) (pour « Tous » et objet
unique, c'EST le constructeur de résultats). Pour que l'entrée de recherche ne
retombe pas sur « Attendre » au passage, sa condition vérifie aussi
QueryResultsPartialReady. AuctionFocusAfterResults choisit le curseur : la
première entrée qui n'est ni le champ de saisie, ni « aucun résultat », ni
« Attendre » ; s'il n'y en a pas, le champ de saisie. L'annonce de zéro résultat
part en UNE seule sortie (« aucun résultat;Suchbegriff eingeben ») - un second
OutputStringBTtts aurait coupé le premier. Dans la liste de catégories du scan
complet, l'entrée zéro résultat s'appelle également « aucun résultat » ; la
branche sans aucune donnée de scan reste « vide », car c'est un autre constat.
Hôtel des ventes : un scan complet annulé bloquait ensuite toute recherche
Simple : Annuler un scan complet en cours en fermant l'hôtel des ventes avait pour
effet que TOUTE recherche et toute catégorie annonçaient ensuite « Nicht
möglich, scan läuft » - alors qu'aucun scan ne tournait plus. Cela persistait
jusqu'à ce qu'on relance un scan complet et qu'on le laisse aller au bout. La
fermeture démonte désormais correctement le scan et la recherche suivante
fonctionne normalement. (Le verrou serveur de 16 minutes sur le prochain scan
complet n'est pas concerné : il est côté serveur et s'applique aussi après une
annulation.)
Technique : AuctionHouseResetQuery refuse la réinitialisation tant qu'un scan getAll
tourne, sauf si l'appelant insiste avec force - cela protège un scan complet en
cours d'être tué par une réinitialisation incidente (menu de vente, chemins de
navigation). AUCTION_HOUSE_CLOSED appelait AuctionScanFinish SANS force : fermer
en plein scan laissait donc state = « waiting » avec QueryData.getAll = true ;
c'est exactement ce que teste la logique de refus de la recherche, si bien que
toute requête ultérieure était rejetée en « getAllRunning ». La fermeture force
désormais - elle EST la fin du scan, et plus aucun événement n'arrive ensuite.
Deux lacunes de journalisation qui masquaient le bug sont corrigées au passage :
le refus dans AuctionHouseResetQuery écrit maintenant sa propre ligne
(auparavant on ne voyait que l'appel juste avant et l'on croyait l'état nettoyé),
et AuctionScanFinish ne signale « finished » qu'APRÈS la réinitialisation, avec
reset=true/false - le journal affirmait jusqu'ici une fin de scan qui n'avait
jamais eu lieu.
Hôtel des ventes : première annonce du scan complet au bout de 5 secondes
Simple : Pendant que le scan complet attend la réponse du serveur, Sku annonce
« scan complet, n secondes » toutes les 10 secondes. La toute première de ces
annonces n'arrivait donc qu'au bout de 10 secondes - dix secondes durant
lesquelles on ignorait si le scan tournait seulement. La première confirmation
arrive maintenant après 5 secondes, la deuxième 10 secondes plus tard, puis au
rythme de 10 secondes (soit à 5, 15, 25 ... secondes). Rien d'autre ne change :
les messages de 25 pour cent de la phase de lecture et l'annonce finale restent
identiques.
Technique : Le rythme dans le ticker OnUpdate de l'hôtel des ventes se trouve
désormais dans tFullScanSpeakNext, qui démarre à tFullScanSpeakFirst (5) et
bascule sur tFullScanSpeakEvery (10) après la première annonce. Le compteur est
diminué du seuil en vigueur plutôt que d'un 10 fixe, afin que la phase reste
juste. À la fin de la phase de travail, le seuil retombe à 5 - sinon le scan
suivant n'aurait de nouveau donné sa première confirmation qu'après 10 secondes.
Hôtel des ventes : le scan complet mourait parfois en silence juste après l'annonce de départ
Simple : Parfois, « Scan complet démarré » n'était suivi de rien : pas d'annonce de
progression toutes les 10 secondes, pas de message de fin, et les menus censés être
verrouillés pendant un scan ne l'étaient pas - le verrou de 16 minutes était
pourtant consommé. Cela touchait surtout ceux qui attendaient la fin du délai
directement à l'hôtel des ventes ouvert. En interne, le scan était tué aussitôt
après le départ ; il démarre maintenant toujours avec une horloge remise à zéro.
Technique : Prouvé dans le journal de débogage du 2026-08-26 : QueryAuctionItems et
« watchdog: getAll timeout 600s » dans la MÊME seconde. Les compteurs du chien de
garde du ticker de scan étaient déjà pleins avant le départ : tTime accumulait tout
le temps d'inactivité à l'hôtel des ventes ouvert (la branche idle ne le remettait
jamais à zéro - l'attente de 16 minutes > 600 s suffisait à elle seule), et
tFullScanElapsed survivait à chaque fin de scan parce que sa remise à zéro se
trouve derrière la barrière de tick de 0,2 s de la branche de sérialisation, qu'une
sérialisation rapide ne franchit jamais. Le premier tick du chien de garde après le
départ additionnait les deux et tuait le scan tout frais ; le chien de garde se
remettait alors lui-même à zéro, d'où le « la plupart du temps ça marche ».
SkuCore.AuctionScanWatchdogReset remet maintenant les deux compteurs à zéro après
chaque QueryAuctionItems réellement envoyé, et la branche idle remet elle-même
tTime à zéro (ce qui protège aussi le chien de garde de blocage des recherches
paginées). Vérifié le 2026-08-26 : un scan après ~28 min d'attente à l'hôtel des
ventes est allé au bout, lisant 173841 enchères.
Clients français : la liste des points de passage était vide dans plusieurs zones (correction de Naxedim)
Simple : Dans les Cavernes des lamentations, dans le Tram des profondeurs, au Repaire des
Grumegueules et au Repaire d'Onyxia, Sku annonçait une liste de points de passage
vide alors que la zone est entièrement cartographiée. Deux observations ont permis de
cerner le problème : un itinéraire déjà lancé continuait de fonctionner, et les mêmes
points de passage restaient sélectionnables depuis l'extérieur de la zone. Il ne
manquait donc aucune donnée : Sku ne savait simplement pas dans quelle zone se
trouvait le personnage. Les clients allemands et anglais n'ont jamais été concernés.
Technique : Deux causes indépendantes produisant le même symptôme, toutes deux trouvées,
corrigées et vérifiées en jeu par Naxedim sur un client français (PR #8).
Premièrement, SkuNav/Geo.lua compare quelques noms de zone MOT POUR MOT avec
GetMinimapZoneText() ; quatre d'entre eux avaient été traduits librement dans
locales/frFR.lua, plus aucune comparaison ne correspondait et GetBestMapForUnit
renvoyait la carte du continent au lieu de celle de la zone. Les nouvelles valeurs
ont été lues avec C_Map.GetAreaInfo() sur un client français, elles ne sont pas
traduites. Deuxièmement, les instances dont la vraie ligne de zone est commentée dans
SkuDB/assets/maps.lua dépendent d'une ligne synthétique (id à partir de 100000) pour
laquelle C_Map n'a rien à résoudre ; elle restait en anglais alors que le dernier
recours de GetCurrentAreaId la compare au nom de CARTE localisé - aucune
correspondance, aucun areaId, aucun continent, liste vide. Ces lignes prennent
désormais leur nom de l'uiMap qui porte le même nom anglais ; en français il s'agit
d'exactement deux zones, le Repaire d'Onyxia et les Cavernes des lamentations. Ajouté
ensuite : un garde-fou pour qu'aucune ligne synthétique ne reçoive un nom qu'une
vraie ligne porte déjà (Joug-d'hiver), la mention « ne pas traduire librement » sur
tous les noms de zone comparés mot pour mot dans locales/enUS.lua, et un magasin de
traduction français indexé par la clé et non plus par le numéro de ligne - il avait
glissé d'une ligne et aurait décalé presque toute la table à la prochaine
régénération.
Au passage, pour les clients anglais : L["Die Höhlen des Wehklagens"] contenait « The
Wailing Caverns », ce qu'aucun client ne dit. Le même cas particulier y était donc
mort lui aussi et vient d'être corrigé.
Menu des quêtes : « Membres du groupe » passe en dernier
Simple : Avec Questie chargé, Sku affiche en plus les quêtes des membres du groupe.
Cette entrée se trouvait jusqu'ici entre « Quêtes actuelles » et « Base de données
des quêtes » - et comme elle apparaît et disparaît avec le groupe, la base de
données passait de la deuxième à la troisième place selon que vous étiez en groupe
ou non. Qui s'y rendait de mémoire n'atterrissait pas au même endroit seul et en
groupe. « Membres du groupe » est désormais toujours en dernier, en troisième
position ; les deux premières entrées ne bougent plus, seul comme en groupe.
Technique : Dans SkuQuest:MenuBuilder, le bloc tSpecs conditionnel (showGroupQuests +
QuestieReady + IsInGroup) passe après le bloc « Base de données des quêtes ».
SkuMenu:Build rend les specs dans l'ordre du tableau, sans aucun tri : l'ordre du
code source est donc l'ordre du menu. L'entrée elle-même, sa condition et ses
sous-menus ne changent pas.
-------------------------------------------------------------------------------------------------------
Changements dans Sku v43.0
Aperçu
Nouveautés
Shift-F9 à Shift-F12 ont désormais leurs propres entrées de raccourcis, et les accès rapides 1 à 4 sont de nouveau libres.
Les annonces de vol en taxi peuvent être désactivées, et elles ne nomment plus jamais le point de départ ni celui d'arrivée comme atterrissage possible.
Nouvelle condition d'aura « Buff debuff vos sorts uniquement » qui filtre les listes de buffs et de debuffs de cette aura sur les effets que vous avez lancés.
Nouvelle commande /skucheck qui vérifie des règles fixes sur l'état réel du jeu ; elle reprend aussi les outils de base de données comme sous-commandes (wp, db, mem).
L'installateur collecte tous les journaux pour un rapport de bogue en un seul bouton.
L'écran de connexion est désormais utilisable avec l'outil de connexion.
Les listes de points de passage annoncent l'avancement du chargement.
Changements
Les avertissements d'expiration d'aura arrivent à l'heure au lieu du prochain événement de combat.
L'évaluation des auras par événement de combat est nettement moins coûteuse.
Annuler la navigation ne passe plus que par une seule touche.
Le moniteur de ressource peut suivre la barre que le jeu affiche : un druide obtient ainsi mana, rage ou énergie selon sa forme.
Le journal de l'outil de connexion survit à un redémarrage de l'outil.
Les données de points de passage sont utilisables bien plus tôt après la connexion, votre propre continent d'abord, et occupent environ 22 Mo de mémoire en moins ; la partie des données d'itinéraires que votre version de jeu ne lit pas n'est plus construite du tout.
Les auras sont désormais interchangeables entre langues : une aura fonctionne sur tous les clients.
Les ensembles d'auras partagés arrivent utilisables chez un équipier d'une autre langue.
Tous les rangs d'un sort comptent pour une seule entrée d'aura.
Les noms de sorts attribués plusieurs fois par le jeu se distinguent maintenant dans les listes de sélection.
Les ensembles d'auras fournis ne sont plus réservés aux clients allemands.
Le menu dans lequel on construit une aura a été refait : liste d'attributs triée par nom, conditions à deux valeurs en bascules, sorts saisissables autant que sélectionnables, et toutes les listes de valeurs s'ouvrent plus vite.
Chaque entrée de menu de tout l'addon coûte environ 13 fois moins à créer, ce qui ouvre les grandes listes de sorts environ trois fois plus vite.
Une liste dont la construction a été interrompue par le serveur est poursuivie à la frame suivante au lieu de rester incomplète sans le dire.
Les réglages à deux valeurs annoncent leur valeur avec le nom et se basculent avec Entrée ; la flèche droite et le sous-menu disparaissent.
Les curseurs de volume du menu Sku règlent directement le réglage du jeu ; Sku n'en garde plus de copie.
Corrections
Clients français : tous les sons d'auras et les clips vocaux enregistrés étaient muets (correctif de Naxedim).
Clients français : la vérification de distance n'annonçait rien et son menu levait une erreur (correctif de Naxedim).
Clients français : deux suites du même rapport corrigées.
Neuf libellés traduits signifiaient autre chose selon la langue (correctif de ZenqFR).
Auras : certaines places de groupe étaient annoncées par un bip au lieu d'un mot.
Auras : une aura réglée sur « une fois » pouvait se déclencher plusieurs fois de suite en combat dense.
Menu : sur les grandes cartes, la liste de points de passage de Shift-F9 ne s'ouvrait qu'au deuxième essai.
Les clés du porte-clés étaient annoncées comme « Vide ».
Sur les royaumes hardcore, Sku termine désormais le chargement de ses points de passage au lieu de s'arrêter en silence.
Navigation : la liste des itinéraires pouvait rester vide et muette lorsque les points de passage étaient reconstruits pendant qu'un menu était ouvert.
Des données d'itinéraires manquantes sont désormais signalées au lieu d'apparaître comme une liste vide.
Une fenêtre surgissante ouverte en combat (invitation de groupe, invocation, libération) pouvait être lue mais pas répondue.
Un itinéraire ne démarrait pas si sa liste finissait de charger pendant que vous y étiez.
L'outil de connexion prenait l'écran de connexion pour la sélection de personnage.
L'outil de connexion : sur les royaumes comptant dix personnages ou plus, la liste des personnages était incomplète, mal numérotée ou refusait de revenir en arrière.
Moniteur de ressource : le réglage « rien » provoquait une série d'erreurs Lua.
Moniteur de ressource : un moniteur désactivé dans le menu des fonctions annonçait quand même les changements de ressource.
Clients français : la liste des enchantements d'arme pour les auras était vide.
La vérification de préparation n'arrivait pas directement sur « Prêt » comme le fait toute autre fenêtre surgissante.
Un volume de jeu réglé dans le menu Sku pouvait revenir à une ancienne valeur sur un autre personnage.
Les quatre profils standard sont créés sans changer de profil entre-temps - sur les royaumes hardcore, cela pouvait vous laisser dans un profil qui n'est pas le vôtre.
Avec nos remerciements à Naxedim et ZenqFR
Les quatre correctifs suivants sont les premières contributions de code directes au dépôt
Sku désormais public. Naxedim a trouvé, analysé et corrigé les deux bogues qui rendaient
muette une grande partie de la sortie audio de Sku sur les clients français ; ZenqFR a
audité les fichiers de traduction, résolu tous les doublons contradictoires côté anglais
et français et amélioré l'outil qui protège contre de nouveaux doublons. Tout le mérite de
ces correctifs leur revient.
Clients français : tous les sons d'auras et les clips vocaux enregistrés étaient muets (correctif de Naxedim)
Simple : Sur un client français, depuis la v42.11, chaque aura dont la sortie est un son
ne jouait plus rien du tout - pas d'erreur, pas de ligne de journal, juste le
silence - et chaque clip vocal enregistré retombait sans bruit sur la synthèse vocale
(TTS). La cause : depuis que Sku parle français, il cherche aussi un pack vocal
FRANÇAIS, et un tel pack n'existe pas ; la recherche ne trouvait donc rien et tout ce
que le pack vocal fournit devenait muet. La parole ne faisait que se dégrader en TTS,
mais un effet sonore n'a rien vers quoi se dégrader - c'est pourquoi cela ressemblait
à « mes auras ne fonctionnent plus » plutôt qu'à « l'audio a changé ». La recherche de
pack utilise maintenant le même repli vers l'anglais que le reste du système audio
depuis la v42.09. Les clients allemands et anglais ne sont absolument pas affectés.
Technique : Le résolveur de pack vocal dans Core.lua comparait les locales des packs à
Sku.Loc ; depuis que la v42.11 livre locales/frFR.lua, Sku.Loc vaut « frFR » sur un
client français, aucun pack ne pouvait la déclarer, Sku.AudiodataPath restait "" et
VoicePackAudioDir() renvoyait nil - réduisant au silence les 64 entrées de
SkuCore.outputSoundFiles (sons d'auras, son de combat de SkuMob, son de nouvelle
ligne de SkuChat) plus chaque clip vocal du pack. La comparaison se fait maintenant
sur Sku.LocAudio ou Sku.Loc, exactement la règle « vers quelle langue enregistrée
est-ce que je me replie » qu'IntegratedAudioDir() utilise déjà ; Sku.LocAudio vaut
« enUS » sur tout client non allemand, le comportement deDE/enUS est donc identique à
l'octet près. Trouvé sur un vrai client français, corrigé et vérifié en jeu par
Naxedim (PR #5).
Clients français : la vérification de distance n'annonçait plus rien et son menu levait une erreur (correctif de Naxedim)
Simple : Sur un client français, la vérification de distance n'annonçait plus aucune
distance, quelle que soit la cible, et l'ouverture du menu de configuration des
distances produisait une erreur au lieu du menu. Deux causes, toutes deux propres au
français : premièrement, le réglage « prononcer la distance » est enregistré dans la
configuration sous la forme du MOT traduit pour « Parlé » - une configuration écrite
quand Sku tournait encore en anglais (avant la v42.11) contient le mot anglais, que
le client désormais français ne reconnaît plus. Deuxièmement, les clips de nombres
n'existent enregistrés qu'en allemand et en anglais, et le code énumérait exactement
ces deux langues - un client français restait donc silencieux même avec une
configuration française toute fraîche. Le marqueur enregistré est maintenant accepté
dans les trois langues, une valeur inconnue ne peut plus casser le menu, et les clips
se replient sur les enregistrements anglais comme le reste du système audio. Les
clients allemands et anglais sont inchangés : même marqueur, mêmes clips, même audio.
Technique : Un nouveau RangeCheck:IsSpokenSound() accepte « Gesprochen »/« Spoken »/
« Parlé » (plus le L["vocalized"] traduit à chaud), utilisé par DoRangeCheck et par le
constructeur de menu dans Options.lua - ce dernier tombait jusqu'ici dans la branche
else pour une valeur inconnue et concaténait nil depuis SkuCore.RangeCheckSounds, ce
qui levait l'erreur. Le dossier de clips vient maintenant de Sku.LocAudio (hans_de-de
pour deDE, hans_en-us sinon) au lieu d'une chaîne de Sku.Loc qui ne correspondait à
aucune branche en frFR. Les nouvelles entrées écrivent toujours L["vocalized"] ; pas
de migration de données. Trouvé et vérifié sur un vrai client français par Naxedim
(PR #6).
Traductions : neuf libellés signifiaient autre chose selon la langue (correctif de ZenqFR)
Simple : Neuf textes figuraient en double dans les fichiers de traduction avec deux
significations DIFFÉRENTES, et chaque langue pouvait finir par retenir une version
différente. C'était le plus audible sur les clients anglais, où le mauvais jumeau
l'emportait plusieurs fois : la barre d'action de posture était annoncée « Special
Action bar » (une toute autre barre), la touche de débogage « debug mode » au lieu de
« debug output », une catégorie de menu « Features » au lieu de « Values ». Pour
chaque paire, la signification réellement correcte a été déterminée d'après l'endroit
où le texte est utilisé dans le code, et le mauvais jumeau a été supprimé dans les
trois langues - l'anglais, l'allemand et le français sont maintenant d'accord sur les
neuf. Six doublons purement allemands restent volontairement ouverts jusqu'à ce qu'un
germanophone tranche.
Technique : AceLocale résout les clés en double par « le premier gagne » pour la langue
par défaut (enUS) et par « le dernier gagne » partout ailleurs - un doublon à deux
valeurs signifie donc silencieusement autre chose selon la langue. ZenqFR a retracé
les points d'appel réels de chaque clé pour déterminer la valeur survivante (PR #3)
et a appris à dev/rework-docs/_locale_dupes.py à ne comparer que le littéral de
chaîne Lua analysé au lieu de la ligne brute, pour que les commentaires « -- » en fin
de ligne ne comptent plus comme conflits (PR #4) - sur 64 conflits signalés, seuls 30
étaient réels ; enUS et frFR passent maintenant le lint à zéro, deDE avec les six
paires de formulations allemandes laissées à un locuteur natif.
Clients français : deux suites du rapport de Naxedim sur le pack vocal
Simple : Le son nommé « bring » - une cloche, nommée d'après le bruit qu'elle fait -
apparaissait dans les menus français comme « apporter » : la traduction l'avait lu
comme le verbe anglais « to bring ». C'est un nom, pas un mot, et il reste désormais
« bring » comme ses voisins non traduits « brang », « dang » et « drmm ». Par
ailleurs, le diagnostic interne qui note quel pack vocal a été trouvé nomme
maintenant la langue réellement cherchée au lieu de la langue du client - avant, un
client français sans le pack anglais installé aurait affirmé « no voice pack found
for frFR », désignant un pack qui ne peut pas exister.
Technique : locales/frFR.lua remet L["bring"] sur l'onomatopée. Core.lua renseigne
maintenant Sku.AudiodataPathInfo dans une branche else du résolveur à partir de
tWantedLoc (la clé de recherche basée sur Sku.LocAudio) plus la locale du client ;
lisible via /wdeval Sku.AudiodataPathInfo. Le troisième point du rapport de la
PR #5 - frFR traduit le préfixe des libellés de sortie en « aura;son » - a été
examiné et volontairement CONSERVÉ : chaque site de comparaison reconstruit le
préfixe depuis la table de locale active, et les réglages/auras enregistrés
conservent des clés d'identifiants (« sound-brass1 »), jamais le libellé - la
traduction est donc sûre ; une synthèse vocale française pour un libellé français
vaut mieux qu'un clip anglais.
Sacs : les clés du porte-clés étaient annoncées comme « Vide »
Simple : Depuis la v42.13, chaque emplacement du porte-clés disait « Vide » alors que les
clés étaient bien là et restaient utilisables - cela ne s'est vu que lorsque quelqu'un
a vraiment eu besoin du porte-clés. Le déclencheur était le correctif de la banque de
la v42.13 : dans le client du jeu, le porte-clés présente la même particularité que le
conteneur principal de la banque et demandait le même traitement spécial, que seule la
banque avait reçu à l'époque. Les clés sont de nouveau annoncées par leur nom. Le
porte-clés n'affiche en outre plus que les emplacements qu'il utilise réellement au
lieu de 32 fixes - auparavant, des dizaines d'emplacements vides suivaient les clés.
Technique : GameTooltip:SetBagItem(-2, slot) ne remplit rien sur ce client, exactement
comme le conteneur de banque -1. Les noms de clés ne fonctionnaient que grâce au repli
générique SetHyperlink de 67f2132 ; quand la v42.13 a volontairement supprimé ce repli
et n'a basculé que la banque (-1) vers SetInventoryItem, le porte-clés est retombé en
silence sur le SetBagItem vide. tSetTooltipContainerItem lit désormais -2 via
SetInventoryItem("player", KeyRingButtonIDToInvSlotID(slot)) - exactement ce que fait
ContainerFrameItemButton_OnEnter de Blizzard pour le porte-clés. Build_BagsFrame limite
en plus le nombre d'emplacements de -2 avec GetKeyRingSize() (emplacements occupés
arrondis à des rangées complètes de quatre), comme la fenêtre de porte-clés de
Blizzard, au lieu de reprendre les 32 bruts de GetContainerNumSlots.
Diagnostic : nouvelle commande /skucheck qui vérifie des règles fixes sur l'état réel du jeu
Simple : /skucheck parcourt toutes les données des sacs et vérifie que chaque emplacement
occupé fournit un nom prononçable ; le résultat tient en une ligne parlée, par exemple
« Vérification des sacs : 64 vérifiés, aucun problème », avec un renvoi vers le
journal en cas de problème. La commande vérifie les données elles-mêmes, pas les
menus - elle aurait donc signalé le bogue du porte-clés même sans jamais l'ouvrir. La
liste des règles ne grandit qu'avec les bogues corrigés : chaque correctif apportera
désormais sa propre règle de garde, afin qu'une modification ultérieure ne puisse pas
réintroduire le même bogue sans bruit. Depuis cette version, /skucheck est en outre
la seule commande de vérification : les trois outils de base de données sont
devenus des sous-commandes - /skucheck wp vérifie les données de points de passage
et s'exécute avec un /skucheck sans argument, tandis que /skucheck db et
/skucheck mem sont des mesures qui ne s'exécutent que sur demande. Les anciens noms
/skudbwpcheck, /skudbcheck et /skudbmem fonctionnent toujours.
Technique : En fin de LocalMenu.lua ; parcourt tBagSlotListSorted avec la même API de
conteneurs et la même résolution de noms que le menu des sacs. Règle 1 : emplacement
avec un identifiant d'objet mais un texte d'infobulle vide = violation ; chaque
violation est écrite dans l'anneau SkuDebugLog en ligne « skucheck » avec sac,
emplacement, identifiant et lien. La banque fermée est sautée avec une ligne de
journal (pas en silence), le porte-clés est volontairement balayé sur ses 32
emplacements bruts, et les objets pas encore envoyés par le serveur comptent comme
« en attente » et non comme des échecs. Les domaines wp/db/mem appellent
SkuDBTools.RunWpCheck/RunDbCheck/RunMem (SkuDBTools.lua) ; ils s'exécutent en tâche
de fond et annoncent eux-mêmes leur résultat, mais l'écrivent aussi dans l'anneau
en lignes « skucheck » - les violations de points de passage une par une, alors
qu'elles n'étaient lisibles que dans les SavedVariables. /skucheck db indique en
plus combien de jeux de données ont changé depuis la capture précédente. Raison de
ce regroupement : avec trois commandes séparées, on pouvait lancer « la
vérification » et manquer entièrement les règles des points de passage.
Auras : les avertissements d'expiration arrivent à l'heure, pas au prochain événement de combat
Simple : Deux plaintes anciennes avaient la même cause : une aura comme « affaiblissement
sur la cible sous 3 secondes » ou « mon affaiblissement a disparu de la cible »
n'était revérifiée que lorsqu'un AUTRE événement de combat arrivait. Dans un combat
purement au corps à corps, l'avertissement pouvait donc arriver avec un coup d'arme
entier de retard, hors combat avec des minutes - ou ne jamais partir avant
l'expiration elle-même. Sku note désormais, en vérifiant l'effet, QUAND le seuil sera
atteint et réveille la vérification exactement à ce moment ; l'apparition ou la
disparition d'un effet déclenche aussi la vérification directement. De plus, les mots
parlés d'une aura peuvent maintenant dépasser les annonces en attente, comme le font
déjà les sons d'alerte - ils n'interrompent rien et ne chevauchent rien, ils ne se
mettent simplement plus en queue. Les avertissements d'enchantement d'arme en
profitent aussi : au lieu d'un retard pouvant atteindre une seconde, ils partent
désormais à l'image près.
Technique : Trois mécanismes : (1) un planificateur d'échéances dans SkuAuras/Core.lua -
chaque passe d'évaluation note dans tNextDurationDeadline le moment de seuil le plus
proche (expirationTime moins seuil) de toutes les conditions de durée actives
(opérateur « plus petit ») ; le pilote de trame compare un nombre par trame puis
déclenche UNE passe synthétique DURATION_DEADLINE. Remplace la revérification
approchée à la seconde des enchantements d'arme. (2) UNIT_AURA déclenche une
évaluation lors d'un vrai changement d'ensemble des effets (ajout/retrait, pas les
mises à jour de piles ou de durée) - différence de noms contre un instantané,
filtrée contre les passes du journal de combat dans la même trame et contre les
changements de cible. (3) les sorties en mots utilisent le chemin aInstant
d'OutputString, présent mais jamais branché (insertion en tête au lieu d'ajout en
queue) ; un curseur de trame garde les sorties en plusieurs parties dans l'ordre
parlé.
Auras : évaluation par événement de combat nettement moins coûteuse
Simple : En raid, Sku évalue les auras des centaines de fois par seconde. Plusieurs
coûts cachés ont disparu : les conditions étaient vérifiées deux fois, les durées
restantes redemandées au client à chaque événement, et le groupe entier parcouru
requête par requête pour chaque événement. Deux bogues silencieux corrigés au
passage : une aura sans condition « sort en temps de recharge » pouvait annoncer le
nom de recharge d'une AUTRE aura, et les temps de recharge d'objets étaient
réhorodatés à chaque changement de sac. /skucheck gagne pour cela un domaine
« auras » qui vérifie que l'évaluation n'écrit plus de globales.
Technique : Évaluer une fois au lieu de deux (la branche à valeur unique de la boucle
d'attributs tournait deux fois par accident ; plus deux globales fuitées,
tSpellNameOnCdValue injectait des valeurs périmées d'une aura à l'autre). Les durées
restantes viennent d'une extension expirationTime du cache de listes de niveau 2 au
lieu de rebalayages UnitAura (/skuauracache verify vérifie maintenant aussi les
valeurs exp, /skuauracache off désactive les deux). Les GUID de groupe se résolvent
via une carte invalidée aux événements de roster au lieu de boucles raid1..40 par
événement (l'ordre du résultat et l'horizon historique raid1..25 du RoleChecker sont
préservés exactement). targetUnitDistance et targetTargetUnitId sont paresseux,
LogRecorder fait un accès aux réglages au lieu de quatre, et la garde
UNIT_INVENTORY_CHANGED teste le vrai itemID au lieu d'une globale nil.
Installateur : un bouton collecte tous les journaux pour un rapport de bogue
Simple : L'écran d'accueil de l'installateur comporte un nouveau bouton, « Collecter tous
les journaux ». Il crée UN seul fichier zip dans votre dossier Téléchargements
contenant tout ce qu'il faut pour diagnostiquer un problème - et ce pour TOUTES les
versions du jeu détectées à la fois, pas seulement une : le journal de débogage et
d'erreurs de Sku, les captures de BugGrabber/BugSack et de WVDebug, les journaux du jeu
lui-même avec ses erreurs Lua, le fichier de CVar Config.wtf, la liste de tous les
addons installés avec leur version, le journal de l'outil de connexion et celui de
l'installateur. Jusqu'ici il fallait aller chercher ces fichiers un par un dans trois
arborescences différentes, et en oublier un valait une question de plus au lieu d'une
réponse. Le fichier s'appelle Sku-Logs-.zip ; l'installateur en énonce le chemin
complet et ouvre le dossier avec le fichier déjà sélectionné, prêt à être joint à un
rapport de bogue. Une collecte typique pèse environ 7 Mo.
Technique : Nouveau fichier LogCollector.cs dans l'installateur, déclenché par un quatrième
bouton dans UpdatePromptForm (libellé et texte vocalisé dans les trois langues). La
collecte se fait dans un sous-dossier par client : les SavedVariables de la portée
compte ET de la portée personnage (Sku écrit dans les deux), y compris les copies .bak
- celles-ci sont l'état de la session PRÉCÉDENTE et donc souvent la seule copie
subsistante de l'anneau de débogage sur lequel porte le rapport - ainsi que Logs,
Errors, Config.wtf, Sku.toc, .build.info et une liste des addons avec versions de TOC
et marquage des liens symboliques. S'y ajoutent system-info.txt (système, langue, un
bloc par client et la liste de ce qui n'a PAS été inclus et pourquoi - une omission
doit se lire comme une omission et non comme une absence) et le journal de
l'installateur, forcé pour l'occasion du tampon mémoire de Logger vers le disque. Le
dossier Errors est délibérément exempté de la limite d'âge de 30 jours - il ne contient
quelque chose que lorsque quelque chose a réellement cassé, et le premier essai réel
n'en livrait sinon aucun des 21 rapports de plantage présents - et il est plafonné aux
25 plus récents à la place. L'installateur passe ainsi en version 4.2.
Outil de connexion : son journal survit désormais à un redémarrage de l'outil
Simple : L'outil de connexion effaçait son journal à CHAQUE démarrage. Quiconque tombait
sur un bogue, fermait l'outil puis le rouvrait avant de collecter les journaux
détruisait donc la preuve même qu'il s'apprêtait à signaler - et c'est la séquence la
plus courante d'un rapport de bogue. Les cinq dernières sessions restent maintenant à
côté du log.txt courant sous les noms log-1.txt à log-5.txt, et le nouveau bouton de
collecte de l'installateur les emporte automatiquement.
Technique : log.ahk écrit désormais vers un chemin ABSOLU dérivé de l'emplacement du script
au lieu du nom relatif « log.txt », qui se résout par rapport au répertoire de travail
du processus - START.ahk le règle correctement, mais c'est un état global au processus
que n'importe quoi peut changer ensuite, et un journal qui atterrit là où personne ne
regarde ne vaut pas mieux que pas de journal. ClearLogFile() effectue une rotation au
lieu d'effacer. L'empaquetage et le .gitignore excluent log-*.txt exactement comme
log.txt, et les journaux d'une installation existante ne sont jamais touchés par une
mise à jour.
Outil de connexion : l'écran de connexion s'annonçait comme la sélection de personnage
Simple : Quand le jeu n'atteint pas le serveur au démarrage, on n'arrive pas sur la
sélection de personnage mais sur l'écran de connexion, avec nom de compte, mot de
passe et bouton Login. L'outil y menait au menu principal, dont la première entrée
est « sélectionner un personnage » : l'écran qui prouve justement qu'il n'y a aucune
liste de personnages s'annonçait donc comme la sélection de personnage. Qui ne le voit
pas n'entendait rien qui le distingue du vrai. L'outil annonce désormais une fois à
l'arrivée : pas connecté, soit il n'y a pas de connexion au serveur, soit le nom de
compte et le mot de passe doivent encore être saisis dans le jeu. Il remarque aussi
maintenant le changement d'écran : si l'on se connecte à la main, la liste des
personnages est construite aussitôt, au lieu de laisser l'outil muet sur l'ancien
menu.
Technique : « pas de connexion » n'est PAS un écran distinct - vérifié sur le client
2.5.6 en fonctionnement, il est identique au pixel et à l'OCR près à un écran de
connexion normal (mêmes sondes de widgets, checks.login = true, aucune fenêtre). Il
n'y a donc rien à marquer : l'outil n'annonce que ce qui y est toujours vrai, ce
client n'est pas connecté. Le menu est désormais son propre arbre plutôt que le menu
principal, et InitLogin ne s'exécutait qu'au changement de MODE, d'où un changement
d'écran passé inaperçu ; le veilleur déclenche maintenant une réinitialisation
complète dès que l'écran de connexion est atteint ou quitté.
Outil de connexion : l'écran de connexion est désormais utilisable
Simple : Le menu de l'écran de connexion commence maintenant par les commandes de l'écran
lui-même : nom de compte, mot de passe, se connecter, enregistrer le nom de compte -
puis, inchangés, la voix, la langue, le type de jeu, la région et la version, afin que
les réglages restent accessibles même si la connexion échoue. L'outil n'enregistre
RIEN et ne saisit rien : il place seulement le curseur du jeu dans le champ et passe
le clavier au client, c'est vous qui tapez. Il n'y a pas de connexion automatique et
il n'y en aura pas. Le nom de compte est lu à voix haute, « vide » s'il est vide ; le
mot de passe n'est JAMAIS lu. Tant que le clavier appartient à un champ, les flèches
vont au jeu, pour pouvoir atteindre une faute de frappe ; Entrée termine la saisie,
Échap la quitte, et aucune des deux n'est transmise au jeu - se connecter est une
entrée à part, pour que rien ne parte par accident. Fermer et rouvrir Battle.net reste
généralement plus rapide ; la voie manuelle existe pour ceux qui la veulent.
Technique : quatre nouveaux widgets dans data.ini ([Classic] et [BurningCrusade]) :
LoginAccountField 10000,403, LoginPasswordField 10000,478, LoginSubmitButton
10000,531 et LoginSaveAccountCheckbox 26,662 - mesurés sur le client en
fonctionnement en 2880x1800 puis convertis dans l'espace d'interface de 768 de haut,
donc indépendants de la résolution, et recoupés avec des entrées déjà présentes
(« Account erstellen » mesuré à 549 contre les 550 enregistrés). Le contenu du champ
est lu par OCR du CHAMP seul, pour qu'aucune étiquette venue d'ailleurs ne passe pour
du contenu. « Se connecter » suit la tentative : les fenêtres de progression à un
seul bouton sont lues mais PAS cliquées, car leur unique bouton est Annuler et il
coupe la connexion en cours - le même piège que pour l'entrée en royaume ; les
fenêtres à deux boutons sont lues et traitées. Là où les widgets manquent (Cata,
Retail), les entrées le disent et ne font rien.
Outil de connexion : la liste des personnages sur les royaumes de dix personnages ou plus
Simple : Le panneau des personnages en affiche exactement neuf à la fois. À partir du
dixième, la liste défile, et tout a déraillé en même temps : des personnages
manquaient, la numérotation ne correspondait pas à la liste réelle, et le retour au
début de la liste ne fonctionnait pas de façon fiable. Les trois ont la même cause.
L'outil parcourt la liste avec les touches fléchées et observe le déplacement de la
barre de sélection - or dès que la liste défile, la barre ne bouge plus, et « rien
n'a changé » était lu comme « la liste s'arrête ici ». Une touche avalée par le jeu a
exactement la même allure, et c'est elle qui tronquait les listes. Avec neuf
personnages ou moins la liste ne défile jamais, ce qui explique que rien de tout cela
n'ait jamais été visible. L'outil demande maintenant deux fois avant de croire à une
fin de liste, et s'appuie sur le grand nom affiché sous le modèle du personnage pour
trancher - ce nom est celui du personnage sélectionné, quel que soit le défilement.
Il annonce également le compte en cours tous les dix personnages, car une longue
liste représente sinon une demi-minute de silence, et le menu reste utilisable
pendant sa reconstruction au lieu de devenir muet. NON TESTÉ en jeu : il n'existe ici
aucun royaume de plus de neuf personnages. Si la liste reste fausse, le journal de
l'outil nomme désormais précisément la décision erronée.
Technique : Vérifié sur le code d'interface du client lui-même. Blizzard_GlueXML_TBC.toc
charge Vanilla\CharacterSelectConstants.lua avec CHARACTER_SELECT_MAX_CHARACTERS = 9,
et CharacterSelect_OnShow l'affecte à MAX_CHARACTERS_DISPLAYED. Toute condition
d'arrêt du parcours qui reposait sur UNE seule observation en exige désormais deux,
avec une attente plus longue entre les deux, et ne represse qu'après qu'un coup d'œil
a prouvé que rien n'avait bougé - une seconde pression sur une étape déjà enregistrée
saute un personnage sans laisser de trace. La détection du bouclage exige maintenant
l'emplacement 1 exactement, car CharacterSelectScrollDown_OnClick, passé le dernier
personnage, fait CHARACTER_LIST_OFFSET = 0 puis SelectCharacter(1) ; le retour en
haut est en outre attendu avant confirmation. Le repli qui ne lit que la portion
visible remonte désormais d'abord en haut, car UpdateCharacterSelection fixe le
décalage à selectedIndex - MAX_CHARACTERS_DISPLAYED dès que le dernier personnage
joué se trouve sous la limite - les neuf lignes visibles étaient alors les
personnages 6 à 14, annoncés 1 à 9. Les sondes de pixels de la barre de sélection ont
été déplacées vers la gauche : à dix personnages, UpdateCharacterList élargit le
panneau de 260 à 280 et affiche une barre de défilement dont le fond noir recouvre
précisément la bande que la barre de sélection libère. Le menu est maintenant
construit détaché et publié en une seule affectation, comme la liste des royaumes.
Royaumes hardcore : Sku termine désormais le chargement de ses points de passage au lieu de s'arrêter en silence
Simple : Sur les royaumes hardcore, le client de jeu accorde aux addons nettement moins de
temps de calcul par image que sur les royaumes normaux. Sku construit ses données de
points de passage et d'itinéraires juste après la connexion, et c'est précisément cette
construction qui se heurtait à cette limite : le client interrompait purement et
simplement le travail en cours, et Sku ne s'en apercevait pas. Résultat : toutes les
listes de points de passage répondaient « points de passage en cours de chargement »
pendant toute la session - sans erreur, sans annonce vocale, sans entrée de journal -
et recharger l'interface n'y changeait rien. Sku détecte maintenant que le client le
freine, réduit la taille de ses lots de travail et continue au lieu de s'arrêter ; si
un lot est malgré tout interrompu, la construction repart d'elle-même. Le prix à
payer : sur ces royaumes, il faut quelques secondes de plus après la connexion avant
que les itinéraires et les points de passage soient complets. Tout le reste de Sku est
utilisable immédiatement, et les listes indiquent maintenant l'avancement (entrée
suivante). Les royaumes normaux ne changent pas.
Technique : Le client signale l'interruption par le LUA_WARNING « insecure scripts exceeded
execution limit for addon Sku », ou par « script ran too long » levé à l'intérieur de la
coroutine en cours. Sur le royaume hardcore, cela s'est produit à chaque connexion
(52/25/25 avertissements par connexion) et pas une seule fois dans les huit sessions
enregistrées sur des royaumes Era et Anniversary normaux - pour un travail identique
(1936 ms sur Era contre 1955 ms en hardcore pour la même construction).
Sku:BuildFrameBudgetMs est désormais adaptatif : chaque avertissement de ce type divise
par deux le plafond par image (150 -> 75 -> 37,5 -> 20 ms), ce dont profite toute la
séquence de construction d'après-connexion, pas seulement le cache des points de
passage. La construction n'est plus rythmée par une chaîne C_Timer qui se réarmait
elle-même - c'est justement ce réarmement que l'interruption avalait, laissant la
coroutine suspendue pour toujours sans personne pour la reprendre - mais par un pilote
OnUpdate que le client appelle lui-même à chaque image : un lot interrompu coûte une
image au lieu de toute la construction. Si la coroutine meurt malgré tout, la
construction est relancée jusqu'à trois fois, et l'étape de construction remet sa
demande en place au lieu de la perdre dans un pcall.
Navigation : une liste d'itinéraires pouvait rester vide sans dire pourquoi
Simple : un testeur sur un royaume hardcore a signalé que la liste d'itinéraires de
Shift-F10 annonçait seulement « liste vide » à un endroit où il y a des itinéraires.
Derrière cela se cachaient deux chemins différents par lesquels une liste pouvait
devenir vide sans que rien ne l'indique.
Premièrement : depuis que Sku redémarre sa construction de points de passage sur les
royaumes hardcore lorsque le client l'interrompt (voir ci-dessus), les points de
passage peuvent être remplacés pendant qu'un menu est ouvert. Une liste issue de la
construction précédente pointait alors vers un point de passage qui n'existait plus.
Cela levait une erreur en plein milieu de la construction des sous-entrées, et comme
cette erreur était capturée, le niveau restait simplement vide : aucune entrée, aucune
indication, aucun message d'erreur, rien à entendre.
Deuxièmement : si les données d'itinéraires elles-mêmes manquaient, Sku annonçait
malgré tout « chargé » et toutes les listes d'itinéraires étaient vides - là encore
sans la moindre indication. Les deux cas sont désormais visibles : la liste répond
correctement au lieu de rester muette, et des données d'itinéraires manquantes sont
annoncées comme n'importe quelle autre erreur de base de données.
Technique : trois endroits, tous de la même forme - un échec ne doit pas rester invisible.
1. SkuNav:GetAllMetaTargetsFromWp5 accédait au point de passage de départ (.worldX)
sans vérification. Après une reconstruction du cache de points de passage - nouveauté
de la v43.0, puisque la construction se relance d'elle-même sur les royaumes hardcore
et que CleanupWaypoints supprime les points de passage sans liaison - ce nom peut
avoir disparu. La fonction renvoie désormais une réponse vide et écrit une ligne de
journal avec la génération du cache.
2. SkuOptions:VocalizeCurrentMenuName et le parcours de chemin appellent BuildChildren
dans un pcall afin qu'un constructeur de sous-menu défectueux n'avale jamais
l'annonce du NOM de l'entrée. Cela reste ainsi - mais l'erreur est maintenant
journalisée avec le nom du nœud, et le nœud est marqué (buildChildrenFailed). Sans
cela, un niveau à zéro entrée était indiscernable d'un niveau réellement vide.
3. Sku:EnsureData ignorait sans un mot une globale de constructeur qui n'est pas une
fonction, puis déclarait quand même le jeu de données prêt. C'est la pire forme
possible : toutes les protections du reste du code demandent « est-ce prêt », pas
« est-ce là ». Pour les itinéraires, cela finit en données de liaison vides, points
de passage d'itinéraire supprimés et menus annonçant tranquillement « liste vide ».
Chaque constructeur manquant est désormais journalisé individuellement ; s'ils
manquent TOUS, le jeu de données est considéré comme en échec et annoncé.
S'y ajoute une ligne de journal dans SkuNav:GetAllLinkedWPsInRangeToCoords : lorsque
Sku ne parvient pas à rattacher la zone actuelle à un continent (grottes, le Tram des
Profondeurs, sous-zones non cartographiées), cette requête abandonne à vide - et la
liste disait alors « liste vide », ce qui sonne comme une affirmation sur les données
alors que ce n'en est pas une. /skuzoneprobe expose le même cas en entier.
Connexion : les données de points de passage des itinéraires sont prêtes bien plus tôt
Simple : Après la connexion, Sku reconstruit tous les points de passage et toutes leurs
liaisons ; jusque-là, les listes répondent « les points de passage sont encore en
cours de chargement ». Plus de la moitié de cette attente partait dans les liaisons,
et les mêmes données étaient parcourues quatre fois. C'est un seul parcours
maintenant, et il commence par le continent sur lequel vous vous trouvez : celui-ci
est terminé en premier, et à partir de cet instant vos listes sont utilisables pendant
que le reste du monde se construit encore. Si vous êtes justement sur le message
d'attente, la première entrée vous est lue automatiquement, sans appuyer sur une
touche. Mesuré sur la même machine : la partie liaisons coûte environ la moitié, et
les listes de votre propre continent arrivent environ 0,7 seconde plus tôt. Les
itinéraires eux-mêmes ne changent pas : mêmes points, mêmes liaisons, simplement plus
tôt. S'y ajoute environ 22 Mo de mémoire en moins pour la gestion des points de
passage, car chaque liaison n'est plus stockée en double - sur les machines
modestes, c'est la moitié la plus perceptible. Et une autre part de l'attente
disparaît complètement : Sku livre deux gros fichiers d'itinéraires, et la version TBC
n'a besoin que des liaisons du second. Ses points de passage étaient malgré tout
construits à chaque connexion, puis jetés sans avoir été lus un instant plus tard ;
ils ne sont désormais tout simplement plus construits. Cela économise environ un quart
de seconde et environ 21 Mo de mémoire de pointe par connexion - sur les royaumes Era,
où le fichier entier est inutilisé, environ 0,4 seconde. Les deux fichiers continuent
d'être livrés en entier : lors du passage ultérieur à Wrath of the Lich King, ce sont
précisément ces données qui deviendront les bonnes, et la sélection s'inversera.
Technique : La partie liaisons de la construction du cache de points de passage était
QUATRE parcours complets sur ~197 000 arêtes orientées : élaguer et symétriser
(CheckAndUpdateProfileLinkData), matérialiser byId/byName, recalculer toute la table à
partir du cache (SaveLinkDataToProfile) et un parcours final sur les ~145 000 entrées
du cache (CleanupWaypoints). C'est un seul parcours désormais : l'élagage se fait au
passage, l'arête inverse est écrite directement dans l'entrée cible - d'où
l'indifférence à l'ordre des continents - et le recalcul disparaît, puisqu'il
réécrivait la table déjà en place. Le seul cas où il pourrait encore changer quelque
chose (une extrémité dont le nom appartient à une autre entrée du cache) est compté et
déclenche l'ancien chemin ; il n'existe pas dans les données d'itinéraires livrées. Le
parcours final reçoit de la passe « custom » la liste des entrées réellement
supprimables, triée par continent, et s'exécute par continent dès que celui-ci est
terminé. Corrigé au passage : l'ancienne passe d'élagage insérait de nouvelles CLÉS
dans la table même qu'elle parcourait avec pairs() - comportement indéfini en Lua ;
les arêtes inverses sont maintenant collectées puis appliquées ensuite. De plus, la
séparation « son propre continent d'abord » ajoutée en juillet n'était jamais active à
la connexion : elle résout la zone via GetMinimapZoneText, encore vide au démarrage de
la construction - le continent est désormais résolu à nouveau avant la partie
liaisons. Vérifié par une réimplémentation des deux variantes sur les vraies données
(dev/rework-docs/simulate_link_build.py : cache identique, table de liaisons
identique, pour chaque continent) et en jeu par /skucheck wp, qui vérifie désormais
aussi que chaque liaison possède son arête inverse.
Technique, deuxième partie : chaque liaison était conservée deux fois - sous l'indice de
cache du point cible (byId) et sous son nom (byName). byName a disparu et les 14
points d'appel utilisent byId ; le nouveau WaypointCacheGetIdForIndex est le
pendant de WaypointCacheGetIdForName. L'écriture dans la table de liaisons est même
devenue moins coûteuse : elle retraduisait chaque nom de cible en identifiant, et
elle dispose maintenant directement de l'entrée. Mesuré : environ 70 000 tables et
224 000 chaînes en moins, 22 Mo de moins dans le rapport mémoire (/skucheck mem), et
encore ~40 ms de gagnées sur la partie liaisons de la construction. Pourquoi ce
doublon : une migration jamais terminée - Sku 32.31 contient déjà, en commentaire,
l'appel censé écrire la table byName directement dans la table de liaisons
enregistrée ; byId est arrivé plus tard comme chemin rapide pour la recherche
d'itinéraire, et les deux sont restés. Trois bogues latents sont partis avec :
une boucle de suppression de point qui ne nettoyait que le point supprimé
contre lui-même, des identifiants construits sur « .areaid » au lieu de
« .areaId » (donc toujours avec la zone 1), et un accès à Links[nil] pour les
points temporaires.
Technique, troisième partie : chacun des deux fichiers d'itinéraires était UN seul
constructeur qui bâtissait tout le fichier. Mesuré en jeu : 375 ms pour le fichier
WotLK et 355 ms pour le fichier de base, à chaque connexion. Sur TBC la navigation
utilise les points de passage du fichier de base et, du fichier WotLK, uniquement les
LIAISONS (l'union des deux graphes, depuis juillet) - LoadDefaultMapData remettait à
nil les points de passage, niveaux et numéros de séquence WotLK juste après la fusion.
Cela représente 71 % des octets de ce fichier, construits puis jetés sans lecture. Les
fichiers sont maintenant emballés par SECTION (dev/rework-docs/_wrap_deferred.py, mode
« sections ») : un constructeur par section de premier niveau de routedata.global,
chacun contenant une tranche exacte, octet pour octet, des données d'origine - l'outil
vérifie que les tranches se recomposent en le fichier source, rien n'est
re-sérialisé. SkuDeferredData.lua choisit les constructeurs selon la version du jeu
(TBC : toutes les sections du fichier de base plus les liaisons WotLK ; Era : le
fichier de base seul) et met à nil les globales des constructeurs ignorés, sans quoi
leurs constantes de texte source resteraient en mémoire. Nouveau : /skucheck routes.
La vérification est conçue pour qu'une sélection erronée soit bruyante au lieu de
coûter silencieusement la moitié du graphe : après EnsureData, plus aucune globale
SkuDBBuildRoute* ne doit exister - une survivante signale une section que la sélection
ne connaît pas -, les tables que lit la navigation doivent être présentes et non
vides, et selon la version du jeu c'est exactement la bonne moitié qui doit manquer.
Les listes de points de passage indiquent désormais l'avancement du chargement
Simple : Le message « points de passage en cours de chargement » avait été écrit pour une
attente d'une ou deux secondes. Quand le client freine Sku (voir l'entrée précédente),
il peut rester affiché dix secondes ou plus, et il devient alors impossible à
distinguer d'un blocage. Il indique maintenant un pourcentage, par exemple « points de
passage en cours de chargement, 47 % ». Le nombre bouge dès le début : les premières
étapes sont les parties de la base de données que la construction doit attendre (0, 20,
40 pour cent), puis la construction elle-même compte jusqu'à 99. Chaque appui de touche
sur l'entrée annonce la valeur actuelle, et dès que tout est là, le premier vrai point
de passage est annoncé automatiquement comme avant.
Technique : L'étalon est le temps de travail d'une construction COMPLÈTE, que chaque
exécution réussie enregistre dans les réglages globaux de SkuNav (wpcTotalWorkMs). Ce
qui est mesuré, c'est le travail, pas le temps réel : le travail est stable, le temps
réel varie maintenant avec la réduction du budget. Le pourcentage reste donc honnête
sur n'importe quelle machine ; seule la toute première construction après une nouvelle
installation utilise la valeur par défaut. Coût par image : une comparaison d'entiers -
seul un pourcentage MODIFIÉ reconstruit le texte, et uniquement pour une entrée sur
laquelle se trouve le focus. L'entrée porte pour cela un marqueur, car son nom n'est
plus un texte fixe : les deux endroits qui la reconnaissaient au nom - le verrou
« non sélectionnable » et le rafraîchissement automatique à la fin de la construction -
testent maintenant le marqueur.
Shift-F9 à Shift-F12 : des raccourcis nommés, et quatre accès rapides retrouvés
Simple : quatre actions de Sku - les deux listes rapides de navigation (Shift-F9 et
Shift-F10), le menu des barres d'action (Shift-F11) et l'annulation de la navigation
(Shift-F12) - étaient câblées directement sur les touches des accès rapides du menu
audio 1 à 4. Cela avait deux conséquences. Ces quatre accès rapides étaient morts :
leurs touches de définition (Ctrl-Shift-F9 à Ctrl-Shift-F12) enregistraient toujours un
chemin de menu et le confirmaient même à voix haute, mais aucune touche ne pouvait
plus le rappeler. Et les quatre actions ne pouvaient être déplacées que en réassignant
une entrée nommée « accès rapide au menu audio 1 » à « 4 » - ce n'est pas là que l'on
cherche « annuler la navigation ». Chaque action possède maintenant sa propre entrée
dans le menu des raccourcis, sous son propre nom, toujours sur Shift-F9 à Shift-F12.
Les accès rapides 1 à 4 sont de nouveau libres et non assignés, exactement comme les
emplacements 5 à 10. Vos réglages existants sont repris à la première connexion : la
touche qui se trouve aujourd'hui sur l'accès rapide 1 à 4 continue d'exécuter la même
action - elle se déplace simplement vers la nouvelle entrée, rien ne change pour vos
doigts. Un emplacement que vous aviez volontairement vidé reste vide.
Technique : le OnClick de SkuZOptions/Core.lua interceptait SKU_KEY_MENUQUICK1..4 et
retournait avant la boucle générale de sélection rapide située plus bas dans le même
gestionnaire. Les trois actions déplacées s'appellent désormais
SKU_KEY_NAVWAYPOINTSQUICK, SKU_KEY_NAVROUTEDESTINATIONSQUICK et
SKU_KEY_ACTIONBARSOPEN, chacune distribuée par le module qui la possède ; les deux
listes de navigation ont rejoint le groupe « Navigation et points de passage ». Une
migration unique dans SkuKeyBindsUpdate déplace key et key2 de l'ancien emplacement
vers la nouvelle constante ; elle se détecte à l'absence de la nouvelle constante dans
le profil, elle s'exécute donc exactement une fois et avant le remplissage des valeurs
par défaut. Les chemins de sélection rapide enregistrés sont conservés : assigner une
touche à l'emplacement 1 à 4 restaure l'ancien raccourci. Nouveau garde-fou :
/skucheck keys signale toute touche détenue par deux assignations Sku à la fois (sauf
les touches d'écrasement transitoires, qui la partagent par conception).
Annuler la navigation passait par deux touches différentes
Simple : il existait deux façons d'arrêter de suivre un itinéraire ou un point de passage,
et elles ne se comportaient pas pareil. L'assignation correctement nommée « arrêter de
suivre l'itinéraire ou le point de passage » était livrée sans aucune touche, et quand
on lui en donnait une, elle annonçait « suivi arrêté » - les mêmes mots que l'arrivée
automatique, impossible donc de distinguer les deux - et elle ne disait strictement
rien lorsque ce que l'on annulait était un itinéraire et non un point de passage isolé.
L'autre était le Shift-F12 câblé en dur, qui disait « Navigation annulée » mais
n'existait que comme accès rapide 4. Les deux ne font plus qu'une touche : « arrêter de
suivre l'itinéraire ou le point de passage », sur Shift-F12 par défaut et librement
réassignable, avec le son de confirmation et le « Navigation annulée » distinct. Elle
reste totalement silencieuse quand il n'y avait rien à annuler, et elle fonctionne
toujours en mouvement et en combat.
Technique : la branche OnClick de SkuNav pour SKU_KEY_STOPROUTEORWAYPOINT appelle
maintenant SkuNav:CancelNavigationSilent() et ne parle et ne joue le son 835 que sur un
retour true. Cela supprime l'ancien chemin non protégé, qui déclenchait
SKU_NAVIGATION_STOPPED et le son même lorsque rien ne tournait, et dont l'annonce
venait de EndFollowingWpOrRt et dépendait d'un point de passage sélectionné.
EndFollowingWpOrRt lui-même reste inchangé : ses quinze autres appels l'utilisent comme
démontage avant de sélectionner une nouvelle cible, où effacer les points de passage
temporaires serait faux.
Navigation : un itinéraire ne démarrait pas si sa liste finissait de charger pendant que vous y étiez
Simple : Si vous ouvriez la liste des itinéraires (Shift-F10) pendant que Sku construisait
encore ses données de points de passage, la liste se rafraîchissait au moment où ces
données étaient prêtes - et à partir de là, Entrée sur un point d'itinéraire ne lançait
plus la navigation. Le menu remontait au contraire au niveau des points d'entrée, comme
si la touche n'avait rien fait du tout. Fermer puis rouvrir le menu corrigeait le
problème, ce qui donnait un aspect aléatoire à toute l'affaire. La liste rafraîchie se
comporte désormais exactement comme une liste fraîchement ouverte : Entrée démarre la
navigation. Cela vaut aussi pour la liste des points de passage à proximité (Shift-F9)
et pour toute liste de points de passage qui se rafraîchit à l'arrivée des données.
Technique : le rafraîchissement à la fin de la construction asynchrone du cache des points
de passage reconstruisait le niveau avec un simple children = {} suivi de
BuildChildren. Cela saute ce que fait OnPostSelect pour un niveau de sélection :
initialiser selectTarget sur le niveau et le copier sur chaque enfant fraîchement
construit. Sans cela, tout le sous-niveau hérite de selectTarget = nil, donc l'Entrée
de la feuille tombe dans la branche générique d'OnPostSelect - aucun OnAction ne
s'exécute (RouteFollowOnAction n'a jamais été appelé) et le curseur est placé sur
self.parent, exactement le saut observé. Cette reconstruction se trouve maintenant à un
seul endroit, SkuOptions:RebuildNodeChildren, utilisé par OnPostSelect lui-même, par le
rafraîchissement des points de passage et par celui des listes volatiles - ce dernier en
mode conservation, afin qu'un rafraîchissement en cours ne jette pas une cible déjà
sélectionnée. Deux fils-pièges accompagnent la correction : OnPostSelect journalise et
compte chaque Entrée qui atterrit sur une feuille sans selectTarget sous un niveau de
sélection, et /skucheck menu reconstruit un niveau de sélection jetable pour vérifier le
contrat et rapporte ce décompte de session.
Auras : les places de groupe étaient annoncées par un bip au lieu d'un mot
Simple : Quand une aura annonce l'unité qui a déclenché l'événement, certaines places de
groupe ne produisaient qu'un bref bip au lieu d'un mot - le son que Sku joue quand
l'enregistrement d'un mot lui manque. La place 3 et toutes les places « cible de »
étaient concernées. Ces indications sont désormais découpées en mots que le pack
vocal fournit à coup sûr : « party 3 », « cible party 2 ». Toutes les autres unités
continuent de dire leur nom traduit.
Technique : sourceUnitId et destUnitId passaient le jeton d'unité brut à la sortie audio
comme UN seul mot. Les packs livrent party0/1/2/4, mais pas party3 ni le moindre
fichier partyNtarget ; le reste retombait donc sur le bip missingAudio (party3 à lui
seul en comptait 129). tUnitIdToSpokenName dans SkuAuras/data.lua découpe les jetons
party ; les autres jetons retombent sur leur friendlyName traduit issu des listes de
valeurs, puis seulement sur le jeton brut. Corrigé au passage : notifyChat affichait
la référence de table au lieu de l'unité.
Auras : une aura réglée sur « une fois » pouvait se déclencher plusieurs fois de suite en combat dense
Simple : Une aura censée signaler UNE seule fois par application - typiquement « Mot de
l'ombre : douleur expire dans une seconde » - pouvait produire quatre sons en une
seconde lors d'un combat de boss. Cela arrivait quand plusieurs joueurs avaient le
même effet sur la cible, ou quand la durée restante n'était pas lisible du tout lors
d'un passage : Sku l'interprétait à tort comme « l'effet est de nouveau frais » et
levait le verrou « une fois ». Sans lecture, le verrou reste désormais fermé. Un
vrai rafraîchissement ou une nouvelle application le lève toujours immédiatement.
Technique : La formule de réinitialisation de la condition de comptage réarmait used à
chaque passage où les autres conditions tenaient et où la condition de durée
« plus petit » lisait false - et un passage SANS lecture (nom absent de la liste, pas
d'entrée exp, ou la carte exp répondant avec l'aura de même nom d'un autre lanceur)
remplit exactement cela. tSmallerDurationNoRead dans SkuAuras/Core.lua empêche la
réinitialisation en l'absence de lecture ; la nouvelle ligne de journal
« aura gate re-armed » consigne chaque levée. Observé : quatre déclenchements en
987 ms (DURATION_DEADLINE, puis deux fois UNIT_POWER et SPELL_PERIODIC_DAMAGE).
Menu : sur les grandes cartes, la liste de points de passage de Shift-F9 ne s'ouvrait qu'au deuxième essai
Simple : Sur les cartes comptant beaucoup de points de passage, Shift-F9 restait sur
l'entrée du haut au lieu d'ouvrir la liste ; une pression de flèche supplémentaire
l'ouvrait ensuite. La cause : un travail effectué trois fois au lieu d'une. Le saut
construisait la même liste - environ 1900 entrées sur certaines cartes - trois fois
dans une seule image, et le client du jeu interrompait tout via sa limite de temps de
script. Cette interruption est silencieuse, d'où l'impression qu'il ne se passait
rien. S'y ajoutait que chaque appui de touche sur une entrée reconstruisait sa
sous-liste en doublant les entrées. Les deux sont réglés, la liste est construite une
fois. Shift-F10 n'a jamais été concerné, cette liste étant limitée à dix destinations.
Technique : SlashFunc construisait les enfants trois fois : avant la comparaison de nom,
dans le OnSelect de la boucle, puis encore dans la phase finale. La comparaison se
décide maintenant AVANT la construction, et la phase finale saute sa resélection
quand la boucle a déjà sélectionné ce même nœud (nœuds actionOnEnter exclus : là, les
deux appels diffèrent réellement). VocalizeCurrentMenuName ne construit plus que s'il
n'y a rien à compter - il n'a besoin des enfants que pour le marqueur « ;plus », et la
plupart des constructeurs AJOUTENT via InjectMenuItems au lieu de remplacer (mesuré
1859 -> 3718 -> 5577 entrées en parcourant). Suite du test : les niveaux de fenêtre
sous « Local » (Dialogue, Marchand, Quête, Maître de vol) construisent leurs enfants
paresseusement et ne portent pas d'indicateur dynamic ; sans pré-construction ils
ressemblaient à des feuilles sans enfants, et la branche feuille appelle CloseMenu -
or fermer clique le bouton de fermeture de chaque fenêtre d'interactFramesList, si
bien que le saut vers un maître de vol refermait aussitôt sa fenêtre. La construction
a donc lieu quand la liste est vide, sauf si le nœud est dynamic. Deux fils
déclencheurs dans /skucheck menu comptent les deux cas.
Vol en taxi : les annonces peuvent être désactivées - et elles ne nomment plus le point de vol de départ ni celui d'arrivée
Simple : Pendant un vol en taxi, Sku annonce « Prochain point de vol X » puis, environ
800 mètres avant, « Atterrissage anticipé possible à X », afin de pouvoir descendre
plus tôt avec le raccourci. Pour qui vole beaucoup, c'est du bavardage : il existe
désormais un réglage, Paramètres > Général > « Annoncer les points de vol où
atterrir ». Il est activé par défaut et ne coupe QUE la parole - le vol reste suivi,
et la touche d'atterrissage anticipé fonctionne exactement comme avant, avec sa
confirmation vocale, car celle-ci répond à une touche que l'on vient d'appuyer.
Ensuite, plusieurs retours signalaient que Sku nommait le point de vol où le vol
avait COMMENCÉ, ou celui vers lequel il se dirigeait, comme un endroit où atterrir
par anticipation. Ni l'un ni l'autre n'est un atterrissage anticipé : le premier est
l'endroit d'où l'on vient, le second n'est que l'arrivée. Les deux sont désormais
impossibles. Troisièmement, dès que l'atterrissage anticipé a réellement été
demandé, Sku cesse de commenter l'itinéraire. Il nommait auparavant le point
APRÈS celui où l'on était déposé - dix secondes avant de se poser à Ratchet, il
annonçait encore « Prochain point de vol Astranaar », comme si le vol
continuait.
Technique : L'annonce avait un angle mort, le repli sur le point de vol le plus proche.
L'itinéraire est capturé au décollage depuis la carte des vols (le seul moment où
l'interface des itinéraires répond), et toutes les annonces dépendent de lui. S'il
manque, le code se rabat sur « le point de vol utilisable le plus proche » - qui ne
sait rien du vol : peu après le décollage, c'est précisément le point de départ
pendant un moment, et peu avant l'atterrissage, le point d'arrivée. La seule
manière courante de perdre l'itinéraire est un /reload en plein vol : le crochet de
capture s'est déclenché avant le rechargement et ne peut pas se déclencher une
seconde fois. C'est exactement pour cela que le problème était reproductible chez
les personnes concernées et invisible ici. Trois changements : (1) l'itinéraire
capturé survit à un /reload - il est écrit dans les variables sauvegardées du
personnage avec l'étape en cours, puis relu en vol, si bien qu'un rechargement ne
fait plus du tout basculer dans le repli ; (2) le point de départ du vol est
désormais lu lui aussi dans la capture de l'itinéraire (l'emplacement source de la
première étape), donc départ et destination sont connus par leur nom et aucun des
deux ne peut plus être annoncé, dans quelque mode que ce soit ; (3) en mode repli,
un point de vol doit se RAPPROCHER avant d'être proposé comme possibilité
d'atterrissage - un point dont on s'éloigne est derrière soi ; (4) une demande acceptée met fin à l'itinéraire
pour le curseur aussi : le point demandé est la fin du vol, le franchir c'est
arriver, pas faire une étape. La porte de sortie est géométrique et non minutée -
encore en vol mille mètres au-delà signifie que le serveur a ignoré la demande, et
l'itinéraire reprend aussitôt. Le nouveau contrôle
« /skucheck taxi » fixe la règle : un atterrissage anticipé n'est jamais le point de
départ ni le point d'arrivée du vol lui-même. Le réglage est enregistré par profil
sous taxiAnnounceLandingPoints.
Moniteur de ressource : « rien » devient « Ressource actuelle », et elle suit la barre que vous voyez
Simple : Le moniteur de vie & de ressource surveille exactement une ressource, et
c'était un choix figé : mana, rage, énergie - ou « rien ». Mais « rien » n'était pas
un interrupteur, c'était un trou : il provoquait une série d'erreurs Lua, et ce que
le moniteur surveillait à la place n'avait été choisi par personne. L'entrée
s'appelle désormais « Ressource actuelle » et figure en tête de liste. Elle suit la
barre que le jeu vous affiche : un druide obtient le mana en forme normale, la rage
en ours et l'énergie en félin, sans jamais retourner dans le menu. Toutes les autres
classes n'ont qu'une seule barre - pour elles, c'est simplement leur ressource.
Choisir une ressource fixe fonctionne exactement comme avant. Pour désactiver le
moniteur, utilisez « Activé : Non » - c'est à cela que sert le réglage. Si vous
aviez « rien » de sélectionné, vous trouverez le moniteur désactivé et la ressource
réglée sur « Ressource actuelle » ; réactivez-le si vous voulez l'entendre.
Technique : « NOTHING » transmettait l'indice de ressource -1 (Enum.PowerType.None)
directement à UnitPower et UnitPowerMax - une valeur que le moniteur n'a aucune
raison de demander. À sa place vient GetMonitoredPowerType(), qui résout la valeur
enregistrée au moment de la lecture : un choix fixe vers son indice, « Ressource
actuelle » via UnitPowerType("player") vers la barre affichée. Les deux chemins de
lecture passent par là, y compris la comparaison de jeton dans UNIT_POWER_UPDATE -
avec le type automatique, le jeton auquel réagir n'est pas celui qui est enregistré.
Les deux chemins vérifient aussi désormais nil et un maximum de 0 au lieu de
diviser par lui ; c'était la véritable source des erreurs, et comme l'un des deux se
trouve dans le pilote OnUpdate, il ne produisait pas une erreur mais une par tick.
Comme la ressource surveillée peut maintenant changer sous le moniteur, le
pourcentage précédent est réinitialisé à chaque changement de jeton - sinon la
première annonce après un changement de forme serait avalée ou recevrait la mauvaise
direction (hausse/baisse). En outre, le gestionnaire s'arrête si le module est
désactivé : il dépend de deux enregistrements (son propre objet AceEvent et le
répartiteur dans Core.lua) alors qu'OnDisable ne retirait que le premier, si bien
qu'un moniteur désactivé dans le menu des fonctions continuait d'annoncer. Le
nouveau contrôle « /skucheck power » fixe la règle : la ressource configurée doit
toujours se résoudre vers quelque chose que le client peut lire ; une ressource
choisie volontairement mais absente de la classe compte comme « en attente », pas
comme une infraction. La valeur enregistrée « NOTHING » est migrée une seule fois
vers « ACTIVE » à la connexion, moniteur désactivé.
Les auras reconnaissent les sorts à leur identité, plus à leur nom traduit
Simple : Une aura que vous aviez créée était jusqu'ici liée à la langue de votre jeu. La
valeur enregistrée était le nom traduit du sort - « Frostblitz » - et un client anglais
ne connaît que « Frostbolt » : la même aura ne s'y déclenchait donc jamais. Pour la même
raison, un ensemble d'auras partagé en groupe arrivait mort chez un équipier d'une autre
langue, et les ensembles d'auras fournis n'existaient que sur les clients allemands. Sku
retient désormais l'identité du sort, indépendante de la langue. Ce que vous voyez et
entendez reste exactement le nom qu'emploie votre jeu - dans le menu, dans le nom de
l'aura et dans l'annonce quand elle se déclenche. Vos auras existantes sont converties
une seule fois à la première connexion, sans rien faire de votre côté ; leurs noms ne
changent pas.
Un second gain vient avec : tous les rangs d'un sort, ainsi que les variantes lancées
par les ennemis, appartiennent à une seule entrée. « Préviens-moi quand ma cible lance
Éclair de givre » couvre donc les 103 variantes au lieu de la seule qui se trouvait dans
la liste. Savoir si c'est VOUS ou un ennemi qui l'a lancé reste décidé par la condition
« Source de l'événement », pas par le sort.
Là où un nom traduit recouvre plusieurs noms anglais différents - l'allemand
« Verblassen » est à la fois « Fade » et « Fade Out » - la liste ajoute le nom anglais
entre parenthèses pour que les entrées restent distinguables. La liste seulement ;
l'annonce au déclenchement reste courte. Si vous choisissez une telle entrée, c'est la
variante la plus ancienne qui l'emporte : pour « Verblassen », le sort de prêtre et non
« Fade Out ». Les auras déjà enregistrées sous un tel nom restent intactes et
continuent de réagir à toutes les variantes, comme avant.
Technique : L'identité est le nom anglais du sort tiré de SkuDB.SpellDataTBC, déjà livré et
déjà maintenu - aucun nouveau fichier, aucune table d'ID générée (il faudrait la garder
synchronisée avec spells.lua, et une régénération orphelinerait silencieusement les
auras enregistrées). Les valeurs enregistrées portent la marque « spellgroup: » ;
l'affichage vient toujours de SkuAuras.values[key].friendlyName. La liste vivante issue
de UnitAura est ramenée au nom de groupe via la valeur de retour 10 (spellId) ; sur un
client non anglais, le nom localisé reste à côté comme alias de compatibilité pour
qu'une ancienne valeur non convertie continue de correspondre. Sur un client anglais,
nom de groupe et nom vivant sont la même chaîne, l'alias n'est jamais écrit et le coût
est exactement nul. Un ID que SpellDataTBC ne connaît pas - la population la plus
susceptible de dériver au fil du cycle Anniversary - retombe sur le nom localisé,
c'est-à-dire sur le comportement précédent. 838 des 26 603 noms allemands (3,15 %)
recouvrent plus d'un nom anglais. À l'exécution, c'est le groupe dont l'ID de sort est
la plus basse qui l'emporte : les variantes en conflit sont sans exception des ajouts
ultérieurs ; vérifié sur les cas dont la réponse est connue (Verblassen 586 contre
5543, Geschwächte Seele 6788 contre 36788, Schattenwort: Schmerz 589 contre 37603,
Erneuerung 139 contre 37563, Gedankenkontrolle 605 contre 7645). Les valeurs DÉJÀ
ENREGISTRÉES sous un tel nom ne sont malgré tout PAS converties, car elles
correspondent aujourd'hui à toutes les variantes et une conversion supprimerait
silencieusement les autres ; leurs entrées de menu reçoivent le nom anglais en
suffixe. Le nom
affiché d'un groupe provient toujours de l'ID la plus basse qu'il contient, afin qu'il
ne change pas d'une session à l'autre. Nouvelles vérifications sous « /skucheck auras »
: il existe bien des entrées de groupe, tous les ID d'un sort se résolvent vers UN seul
groupe, et la conversion unique n'a laissé aucune valeur derrière elle.
Partage d'ensembles d'auras : nouveau format sans indicateur de langue
Simple : Un ensemble d'auras partagé ne porte plus d'indicateur de langue, et
l'avertissement « l'ensemble est pour une autre langue » disparaît - les conditions sont
désormais indépendantes de la langue. À l'acceptation, chaque aura reçoit en plus son
nom dans VOTRE langue au lieu de garder la phrase de l'expéditeur. Les auras que vous
avez nommées vous-même gardent leur nom.
Technique : C'est SkuAuraSetV2 (charge « AURASET2 », sans champ loc) qui est envoyé ; V1
reste reçu, avertissement compris, car de tels paquets portent encore des valeurs
localisées. Seul V2 est envoyé, volontairement : un paquet V1 supplémentaire déposerait
chez les anciens clients des auras avec des valeurs de groupe qu'ils ne savent pas
résoudre. Le nom est redérivé via BuildAuraName à l'import
(SkuAuras:RelocalizedAuraName) ; les auras customName en sont exclues, et ce sont
justement celles auxquelles d'autres auras peuvent renvoyer : les renvois restent donc
intacts.
Créer une aura : le menu dans lequel on construit une aura a été refait
En clair : Une aura est toujours faite de conditions, et tout ce que vous avez déjà
construit continue de s'évaluer sans changement - mais le chemin vers une condition
est plus court à beaucoup d'endroits, et les listes que l'on parcourt en route sont
triées, courtes et rapides. Dans l'ordre où on les rencontre :
La liste des attributs est triée par nom de bout en bout - y compris les entrées de
groupe, qui se plaçaient auparavant toujours en tête quel que soit leur nom. Une
entrée se trouve donc là où son nom la place, et les premières lettres suffisent pour
l'atteindre. Les 35 événements tiennent derrière deux entrées, « Événements généraux »
et « Événements de sort » ; santé, ressource, mana, rage, énergie, puissance runique
et points de combo sont ensemble dans « Votre santé ou ressource » ; et toutes les
auras que vous avez nommées vous-même sont des conditions sous « Auras créées par
vous » au lieu d'être éparpillées parmi les vrais attributs - la liste du haut
grandissait d'une entrée à chaque aura nommée, et ces entrées se rangeaient dans
toutes les lettres de l'alphabet. Chaque entrée de groupe annonce elle-même ce qui est
sélectionné à l'intérieur : inutile d'y entrer pour le savoir.
Une condition à deux valeurs seulement - « vrai » et « faux », ou « buff » et
« debuff » - est désormais une bascule au lieu de trois niveaux. La valeur figure sur
l'entrée et Entrée la change : « En combat ; vrai », Entrée, « En combat ; faux ».
Pendant la CRÉATION, Entrée va un cran plus loin sur « non défini » puis revient, pour
qu'une bascule posée par erreur puisse être retirée. À comprendre : « non défini »
n'est pas une troisième valeur, cela veut dire que la condition n'existe pas du tout -
c'est l'état neutre, pas « faux ». « faux » est une comparaison comme une autre : « en
combat égal faux » ne se déclenche QU'EN dehors du combat.
Les sorts viennent toujours d'une liste, mais on peut aussi les saisir : « Saisir le
sort » est la première entrée de chaque liste de valeurs de sorts - Entrée, nom ou
numéro, Entrée, et Sku annonce le sort qu'il en a fait. En échange, les deux conditions
« ID du sort » et « ID de l'objet » ont quitté la liste des attributs : elles
désignaient un SEUL rang de sort et signifiaient donc, juste à côté de « nom du sort »,
l'inverse de ce que la même valeur saisie y produit. Nouvelle venue : « Buff debuff vos
sorts uniquement » - réglée sur vrai, les listes de buffs et de debuffs de cette aura
ne voient plus que les effets que VOUS avez lancés, si bien que « mon Mot de l'ombre :
douleur expire » ne signale plus celui d'un autre prêtre sur la même cible. Et comme
« Source (L) » et « cible (L) » étaient régulièrement pris pour votre propre cible,
elles s'appellent maintenant « Source de l'événement (L) » et « Cible de l'événement
(L) » : elles filtrent l'ÉVÉNEMENT déclencheur.
Enfin le comportement des listes : toutes les listes de valeurs s'ouvrent nettement
plus vite, « Sort utilisable (L) » ne fige plus le jeu à l'ouverture, changer le sort
d'une condition de durée se fait sans à-coup et sans continuer d'annoncer l'ancien sort
comme sélectionné, et « Supprimer » demande confirmation avant qu'une aura disparaisse
- c'était la seule entrée de cette liste à ne pas le faire, juste à côté de
« Dupliquer », qui a toujours demandé.
Techniquement : Un attribut devient une bascule quand son type est BINARY, qu'il a
exactement deux valeurs et que son type n'autorise qu'un seul opérateur
(tBinaryValuesForAttribute dans SkuAuras/Options.lua) ; ce qui est écrit est exactement
ce qu'écrivait l'ancienne liste de valeurs, une aura construite par la bascule est donc
indiscernable d'une aura construite à l'ancienne, et une liste values vide sort du
brouillon - c'est cela, « non défini ». La ligne de condition associe actionInPlace et
actionOnEnter, ce qui distingue Entrée de la flèche droite sur le même nœud. La liste
des attributs collecte groupes et attributs avec leur nom affiché et insère les deux
dans l'ordre des noms. Les deux familles d'événements sont UN attribut et UNE condition
stockée : chaque liste de famille propose l'autre comme une seule entrée, et les deux
pointent vers la condition que le brouillon détient déjà - sans quoi passer par les
deux laisserait deux conditions sur `event`, que l'enregistrement fusionne en un groupe
OU toujours vrai. listsOwnOnly est un modificateur binaire : getAuraList récupère le
lanceur (7e valeur de retour d'UnitAura) dans le même balayage et remplit des ensembles
own parallèles qu'EvaluateAllAuras échange par aura marquée. Les listes de valeurs
trient avec une clé de tri par entrée au lieu de deux par comparaison (27 057 au lieu
d'environ 800 000 nouvelles chaînes par liste de sorts), « Sort utilisable (L) »
interroge les 132 emplacements d'action au lieu des 49 000 sorts de la base, et la
condition de durée laisse chaque entrée annoncer son propre état à la lecture
(tValueToggleRefreshLiveName, UNE fonction partagée par référence) au lieu de
réinitialiser 27 000 entrées à chaque Entrée. « ID du sort » et « ID de l'objet » sont
toujours évaluées et seulement masquées du constructeur par un drapeau retired, pour
qu'une aura importée ou ancienne ne tombe pas sur un nil ; avec elles disparaissent
leurs listes de valeurs, environ 49 000 ID de sorts et 25 000 ID d'objets reconstruites
à chaque chargement. Les CLÉS de localisation des conditions renommées sont inchangées,
aucune aura stockée ne bouge donc. La suppression a également imposé de retirer la
branche aName du menu de gestion : selectTarget n'est repointé qu'une fois le niveau
entré, jusque-là la confirmation aurait été cosmétique. Supprimer rafraîchit désormais
aussi les pseudo-attributs d'auras - une aura supprimée restait auparavant proposée
comme condition.
Clients français : la liste des enchantements d'arme pour les auras était vide
Simple : Sur un client français, la condition d'aura portant sur les enchantements d'arme
ne proposait pas une seule entrée, si bien qu'une telle condition ne pouvait pas être
créée.
Technique : SkuAuras:ResolveWeaponEnchantName lisait la colonne 1 de la base
d'enchantements pour enUS et la colonne 2 pour deDE, et laissait le nom à nil sinon -
pour toute autre langue la fonction renvoyait donc nil pour CHAQUE enchantement et la
liste de valeurs sortait vide. Elle prend maintenant la colonne de votre langue, sinon
l'anglaise, comme le reste du système de noms depuis la v42.09. Trouvé lors du portage
vers l'identité de groupe, pas via un rapport.
Menu : chaque entrée coûte désormais environ 13 fois moins à créer
Simple : Les longues listes se construisent nettement plus vite. Cela se voit surtout sur
les listes de sorts d'une condition d'aura : elles comptent 27 057 entrées, et sur un
royaume hardcore elles ne s'ouvraient plus du tout - ce serveur interrompt les scripts
d'addon trop longs, et la construction n'allait qu'à peu près à la moitié. Cela ne
concerne pas que les auras : tous les menus de Sku sont faits des mêmes entrées.
Technique : Une entrée de menu était une copie profonde du modèle SkuGenericMenuItem - par
entrée une table auxiliaire, un parcours pairs() des 28 champs avec un appel type() et
trois comparaisons de clé chacun, 28 écritures, et une copie récursive de la table
children. Une entrée est maintenant une table presque vide dont la métatable pointe
vers le modèle via __index : deux tables et une écriture. Mesuré sur 27 057 entrées :
la création des nœuds passe de 78 à 6 ms (13x), et la construction complète de la
liste de 100 à 31 ms (3,2x, et 2,7x plus rapide qu'avant la refonte des auras). Écrire
un champ sur une entrée masque toujours le modèle comme avant ; children reste une
table propre à chaque entrée, sinon tous les éléments de menu de l'addon partageraient
UNE seule liste d'enfants. Le seul endroit qui copie une entrée (l'entrée de filtre
dans SkuOptions:ApplyFilter) rattache la métatable. /skucheck menu vérifie les deux sur
des entrées jetables.
Mesuré pour comparaison : la refonte des auras elle-même n'a presque pas ralenti ces listes.
Le passage aux groupes a fait croître la liste de sorts de 460 entrées sur 26 597 (+1,7 %),
et les bascules au lieu d'entrées simples coûtent environ 19 % de temps de construction en
plus. La construction était coûteuse bien avant ; sur un royaume hardcore, cependant, une
limite de script est une falaise et non une pente, et ces 19 % ont suffi à la franchir.
Menu : une liste interrompue restait incomplète sans le dire
Simple : Sur un royaume hardcore, le serveur interrompt un script d'addon trop long.
Quand cela touchait la construction d'une longue liste, celle-ci s'ouvrait bien à la
DEUXIÈME flèche droite - mais ce n'était pas une nouvelle tentative, c'était le reste
à moitié construit de la première. Sur les listes de sorts, cela représentait environ
6 900 sorts sur 27 000, sans le moindre avertissement : qui ne trouvait pas son sort
devait en conclure qu'il n'existait pas. La construction reprend désormais à la frame
suivante là où elle s'est arrêtée, et la liste se complète en quelques frames sans que
vous ayez à appuyer sur quoi que ce soit.
Technique : Le budget de script vaut par exécution, la frame suivante en a donc un neuf -
c'est précisément pour cela que la construction des données d'itinéraires est
découpée. SkuOptions:ContinueInterruptedBuild rappelle BuildChildren sur un minuteur à
délai nul tant que le niveau est incomplet. Seul un constructeur qui se déclare via
`resumableBuild` et tient son propre curseur est réinvoqué : un BuildChildren ordinaire
AJOUTE, un second appel créerait donc chaque entrée en double - c'est exactement la
raison d'être de l'ancien verrou. Le curseur est posé APRÈS une entrée, jamais avant,
sinon une entrée empêchée par l'interruption compterait comme construite. Une passe
sans progrès abandonne au lieu de se répéter indéfiniment. /skucheck menu vérifie sur
un niveau jetable qu'une construction interrompue se poursuit au lieu de recommencer.
Les réglages à deux valeurs sont désormais des bascules, plus des sous-menus
Simple : Un réglage qui ne connaît que deux états - « Activé » et « Désactivé », « Oui » et
« Non » - annonce maintenant sa valeur en même temps que son nom : vous entendez par
exemple « Menu Sku en combat ; activé ». Entrée bascule, le focus ne bouge pas, et
vous entendez aussitôt l'entrée dans son nouvel état. La flèche droite n'est plus
nécessaire, car le sous-menu contenant les deux valeurs a disparu. Il fallait
auparavant trois touches - droite, flèche, Entrée - et il fallait y entrer rien que
pour savoir où en était le réglage. Cela concerne tous les réglages, le moniteur de
santé et de combat, les filtres du chat et du journal de combat, le menu des
fonctions, le menu de la caméra et les réglages des autres extensions : environ 150
entrées. Les réglages à PLUS de deux valeurs gardent leur liste, puisqu'il y a
réellement quelque chose à y choisir.
Technique : Nouvel élément de menu partagé, SkuOptions:MakeToggleNode dans
SkuZOptions/templates.lua ; SkuOptions:MakeInPlaceToggle convertit un emplacement
isSelect existant en UNE ligne et continue d'utiliser ses propres GetCurrentValue et
OnAction, afin que chaque particularité d'un réglage reste telle quelle. Peu importe
laquelle des deux valeurs est passée comme « activé », et c'est précisément ce qui a
rendu mécanique la conversion des quelque 50 emplacements Oui/Non existants. Une
bascule est une feuille (actionInPlace, pas de dynamic, pas d'enfants) - d'où
l'inaction de la flèche droite - et un RefreshLiveName relit l'état juste avant chaque
annonce ; l'ancien sous-menu l'obtenait gratuitement en appelant GetCurrentValue à
chaque descente. Trois pièges : le parcours de chemin de SkuOptions:SlashFunc et la
recherche dans les réglages sélectionnent avec aEnterFlag = true et auraient BASCULÉ
une bascule nommée dans un chemin au lieu d'y amener le curseur - les deux se
contentent désormais de placer le focus, et la recherche compare avec toggleLabel
puisque le nom porte l'état. RefreshLiveName ne s'applique pas à l'entrée de filtre
d'une liste : SkuOptions:ApplyFilter la construit en copiant le premier enfant, et la
copie écraserait sinon le texte que vous saisissez. Enfin, une bascule dont le lecteur
lève une erreur est comptée par /skucheck menu - elle sonnerait sinon comme un réglage
sans valeur plutôt que comme un défaut.
Vérification de préparation : la fenêtre n'arrivait pas sur la réponse comme toutes les autres
En clair : Quand quelqu'un du groupe lance une vérification de préparation, Sku ouvre le
menu et y saute, exactement comme pour n'importe quelle fenêtre surgissante. Pour
celle-ci, le curseur n'arrivait pas sur « Prêt » mais un niveau au-dessus, ce qui
laissait deux appuis de touche de plus avant la réponse. Vous arrivez maintenant
directement sur « Prêt », « Pas prêt » est juste en dessous, et la flèche gauche
ramène sur « vérification de préparation ». Qui a lancé la vérification figure sur les
deux entrées en texte complet.
Techniquement : ReadyCheckFrame n'est qu'une enveloppe autour de ReadyCheckListenerFrame,
qui porte le message et les deux boutons ; parcourue génériquement, cette enveloppe
devenait un niveau de menu à part entière et la descente automatique de
SkuCore:CheckFrames y atterrissait. Il existe désormais un constructeur dédié
(SkuCore:Build_ReadyCheckFrame dans SkuCore/LocalMenu.lua, inscrit dans
interactFramesListManual) qui construit les deux réponses à plat en entrées
directAction - le même schéma que Build_RolePollPopup pour le sondage de rôle. Les
libellés viennent des boutons eux-mêmes, que Blizzard traduit dans
ReadyCheckFrame_OnLoad : aucune nouvelle entrée de localisation n'est nécessaire. Seuls
les boutons visibles sont listés : si vous avez lancé la vérification vous-même,
Blizzard masque le côté réponse et il n'y a rien à répondre.
Volumes du jeu : une valeur réglée revenait forte sur un autre personnage
Simple : Si vous baissiez le volume général dans le menu Sku, vous le retrouviez à
l'ancienne valeur sur un autre personnage. Sku conservait en effet sa propre copie des
cinq volumes et des cinq interrupteurs de son, en plus de celle du jeu, et réécrivait
cette copie dans le jeu à chaque connexion. Cette copie dépend du profil Sku, alors
que le réglage du jeu dépend du compte : deux personnages dans des profils différents
entendaient donc deux volumes différents, et une modification faite dans les options
de son de Blizzard disparaissait sans bruit à la connexion suivante. Sku ne fait plus
que régler les valeurs ; c'est au jeu de les retenir, comme pour tout autre joueur.
Les curseurs du menu affichent désormais la valeur réelle du jeu, y compris si vous
l'avez changée ailleurs. Ce que l'on perd : les volumes ne suivent plus un profil Sku.
Technique : SkuOptions:OnEnable réécrivait soundChannels et soundSettings du profil vers
les CVars à chaque connexion, précédé d'une règle spéciale qui prenait une valeur
stockée de 0 ou -1 pour un profil corrompu et réadoptait alors les cinq canaux depuis
les CVars. Tout ce bloc a disparu. Les nœuds de menu dans SkuZOptions/Options.lua
lisent et écrivent les CVars directement (tGetSoundCVarPercent, tSetSoundCVarPercent,
tGetSoundCVarBool) ; les dix clés sont retirées de SkuOptions.defaults et du schéma,
et un passage unique dans OnInitialize les nettoie de chaque profil enregistré - pas
seulement de l'actif, car sans entrée dans les défauts AceDB ne les retirerait plus
jamais. soundChannels reste : SkuChannel, le canal sur lequel Sku diffuse sa propre
sortie, est un vrai réglage Sku. La lecture arrondit désormais au lieu de tronquer -
tonumber("0.35") * 100 vaut 34,999... et serait revenu à 34. Les nouveaux profils
n'apportent plus aucune valeur de volume par défaut ; ce sont les défauts du jeu qui
s'appliquent.
Les quatre profils standard sont créés sans changer de profil entre-temps
Simple : À la première connexion, Sku crée les quatre profils standard s'ils manquent. Il
le faisait jusqu'ici en basculant tour à tour dans chaque profil manquant puis en
revenant dans le vôtre. Sur un royaume hardcore, le serveur interrompt les scripts
d'addon trop longs, et si cela tombait sur cette séquence le personnage restait dans
le profil d'un autre - avec tous les réglages de ce profil au lieu des vôtres. Or un
profil existe dès qu'il porte un nom ; il n'y a rien à basculer pour cela. Dix étapes
lourdes sont devenues quatre étapes courtes qui ne peuvent pas échouer à mi-chemin.
Technique : Les quatre appels à SetProfile plus le retour dans SkuCore/Core.lua sont
remplacés par quatre affectations dans SkuOptions.db.sv.profiles. Le GetProfiles
d'AceDB énumère exactement cette table, et un profil n'est de toute façon rempli par
copyDefaults qu'au premier basculement - l'état enregistré final est le même
qu'auparavant, quatre tables vides. Chaque appel à SetProfile déclenchait au contraire
OnProfileShutdown et parcourait avec removeDefaults et copyDefaults l'arbre complet
des défauts des huit modules, soit une dizaine de parcours dans une seule frame.
Supprimer les basculements supprime aussi le drapeau SkuCore.AutoChange, qui rendait
OnProfileChanged muet pendant ce temps et restait à true en cas d'interruption -
après quoi plus aucun changement de profil ne passait pour le reste de la session. Le
répartiteur enveloppe ce gestionnaire dans un pcall, l'interruption était donc
invisible.
-------------------------------------------------------------------------------------------------------
Changements dans Sku v42.13
Combat : « Annoncer les interruptions sur votre cible » est désormais désactivé par défaut
Simple : Le second des deux interrupteurs d'interruption démarre lui aussi désactivé. Les
deux moitiés de la séparation - votre cible actuelle et tous les ennemis - sont
dorénavant facultatives, car une annonce d'interruption s'impose au milieu de tout ce
que Sku est en train de dire, et la plupart des joueurs ne veulent pas la voir
tourner d'elle-même. L'annonce elle-même ne change pas, seulement le fait qu'elle
soit active si vous n'avez jamais touché au réglage. Pour la réactiver : Combat,
Ennemis, « Annoncer les interruptions sur votre cible ». Comme pour l'interrupteur
« tous les ennemis », cela s'applique une fois aux profils existants, afin qu'une
valeur héritée de l'ancien interrupteur unique ne le laisse pas activé en silence.
Technique : Dans aqCombat.lua, le bloc de migration ne préremplit plus yourTargetInterrupts
depuis l'ancien indicateur outputInterrupts ; la valeur par défaut est false comme pour
allEnemiesInterrupts, et un marqueur unique yourTargetInterruptsDefaultOff nettoie les
profils que des builds 42.13 antérieurs avaient déjà remplis à partir de l'ancienne
valeur. Les valeurs par défaut d'AceDB ne remplissent que les nil, d'où le marqueur
plutôt qu'un simple changement de défaut. tOldOutputInterrupts disparaît avec son
dernier lecteur.
Hôtel des ventes : l'analyse complète s'annonçait comme « enchères démarrées »
Simple : Le démarrage d'une analyse complète de l'hôtel des ventes disait « Analyse des
enchères démarrée, veuillez patienter », ce qui ne dit pas laquelle des deux sortes
d'analyse tourne - une recherche ressemblait bien trop à une analyse complète, qui
occupe ensuite l'hôtel des ventes pendant des minutes. Il est désormais annoncé
« Analyse complète de l'hôtel des ventes démarrée, veuillez patienter », en accord
avec le message de fin, qui dit depuis toujours « Analyse complète de l'hôtel des
ventes terminée ».
Technique : La chaîne de localisation « Full scan started » a été reformulée en deDE, enUS
et frFR. Aucun chemin de code n'a changé ; le point d'annonce dans auctionHouse.lua
reste intact.
Focalisation Sku : l'appel d'une focalisation arrivait en retard et hachait
Simple : appuyer sur une touche de focalisation pour prendre la cible mémorisée annonçait
le nom avec un retard perceptible - souvent commencé, coupé, puis repris. Un appui
produit deux événements, un à l'enfoncement et un au relâchement, et Sku exécutait la
macro de ciblage sur les deux. Le second passage reprenait la même cible, et comme
chaque annonce de cible vide d'abord la file de parole, la ligne à peine commencée
était arrêtée puis relancée - en retard exactement du temps pendant lequel la touche
était maintenue. La macro ne s'exécute plus qu'une fois par appui. Cela ressort
maintenant parce que les touches de focalisation sont sur le pavé numérique par défaut
depuis la v42.13, et ne sont donc utilisées que depuis peu.
Technique : les huit boutons de focalisation dans skuFocus.lua s'enregistraient avec
RegisterForClicks("AnyUp", "AnyDown") et déclenchaient donc la macro sur les deux
fronts. Le cadre de contrôle des touches SET avait le même défaut et était déjà passé à
un seul front ; les boutons GET en avaient alors été explicitement exclus. Ils
s'enregistrent désormais eux aussi en "AnyDown". Il reste dans le journal deux
PLAYER_TARGET_CHANGED et exactement une annonce par appui ; ces deux événements
viennent de /target lui-même, qui efface l'ancienne cible avant de poser la
nouvelle - visible au verrou de ciblage doux, qui écrit deux valeurs différentes (0
puis 2) et non deux fois la même.
Changement de cible : une vérification de portée en double et des boucles de groupe à vide
Simple : à chaque changement de cible, Sku parcourait tout le groupe et tout le raid pour
savoir si la cible combat déjà quelqu'un de votre groupe - y compris en solo, où ni
l'un ni l'autre n'existe. Cela faisait 88 requêtes dans le vide, et avec l'option
« Annoncer les unités contrôlées par un joueur avec des descriptions génériques », plus
de deux mille. De plus, la vérification de portée tournait deux fois par changement de
cible : le second appel se trouvait dans une portion de code qui ne pouvait jamais
recevoir son résultat et n'annonçait donc jamais rien. Les deux ont été retirés. Rien
de ce que Sku dit ne change, et la détection de l'état de combat reste complète : en
groupe ou en raid, tous les membres sont toujours vérifiés, plus votre familier et
vous-même, et la question « qui ma cible attaque-t-elle » est posée comme avant.
Technique : dans SkuMob PLAYER_TARGET_CHANGED, les boucles party1-4 et raid1-40 sont
désormais conditionnées par IsInGroup() / IsInRaid(), et le raid ne va que jusqu'à
GetNumGroupMembers() - la même écriture que dans SkuAuras et SkuQuest. L'ordre est
conservé (groupe avant raid) pour qu'un membre de votre propre sous-groupe réponde
toujours « party N ». Dans SkuNav, la branche aForTarget de GetNonAutoLevel a disparu :
elle commençait par DoRangeCheck, qui ne renvoie aucune valeur, si bien que sa première
condition n'était jamais remplie - il ne restait que l'effet de bord, une deuxième
vérification de portée forcée et un éventuel second son de distance par changement de
cible. Son appelant dans SkuMob et la fonction utilitaire GetCreatureIdFromCreatureGUID,
utilisée nulle part ailleurs, sont partis avec elle.
Cible en combat : l'indicateur restait allumé en permanence pour une partie des utilisateurs
Simple : cela ne concernait que ceux qui avaient activé « Annoncer les unités contrôlées
par un joueur avec des descriptions génériques » (désactivé par défaut). Chez eux,
toute cible était signalée « en combat » - bip ou annonce -, y compris un animal
totalement étranger au combat. Pour une unité que Sku ne peut pas classer, ce qui
inclut une unité qui n'existe pas, la fonction de nom renvoie une chaîne vide. Cette
chaîne vide se retrouvait comme entrée dans la liste des noms de votre propre groupe.
Le test suivant - « ma cible attaque-t-elle quelqu'un de mon groupe ? » - recevait lui
aussi une chaîne vide pour « ma cible n'attaque personne », et celle-ci correspondait à
cette entrée. La réponse était donc toujours oui. Les noms vides ne sont désormais ni
enregistrés ni comparés. Pour les personnes concernées, le son de combat arrivera bien
plus rarement, uniquement quand la cible combat réellement vous-même, votre familier ou
votre groupe. La même cause touchait « Placer automatiquement les marques de raid
privées Sku sur les cibles en combat » : là, pratiquement chaque cible recevait une
marque.
Technique : avec vocalizePlayerNamePlaceholdersSkuTts = true, GetTtsAwareUnitName renvoie
"" pour toute unité qui n'est ni vous, ni votre familier, ni un membre du groupe.
tRosterNames récupérait cette clé vide, et la consultation de targettarget la
retrouvait. Les deux côtés testent maintenant aussi name ~= "".
Auras : une aide de vérification issue de la refonte était livrée activée
Simple : le cache des listes d'auras reconstruisait aussi chaque liste une seconde fois de
zéro afin de comparer les deux résultats. C'était un contrôle de la phase de refonte,
livré activé par erreur - et il coûtait exactement le travail que le cache fait
économiser. Il est désormais désactivé ; /skuauracache verify on le réactive pour une
recherche de panne.
Atlas Loot : saccades à la connexion et à l'ouverture
Simple : depuis peu, le jeu se figeait complètement pendant quatre à cinq secondes
quelques secondes après l'écran de chargement, puis mettait une vingtaine de
secondes au lieu de huit à se calmer. En cause : l'intégration Atlas Loot. À chaque
connexion et à chaque rechargement, Sku parcourait toute la base de données
d'Atlas Loot - environ 40 000 entrées d'un seul tenant - même dans une session où
l'on n'ouvrait jamais Atlas Loot. Cela n'arrive plus à la connexion : cela se
produit quand on ouvre réellement Atlas Loot, et le travail est découpé en petites
tranches en arrière-plan au lieu de bloquer l'image. Si vous n'utilisez pas Atlas
Loot, vous ne payez plus rien pour lui, et ses modules de données ne sont plus
chargés inutilement. Deuxième partie : l'ouverture de la liste de recherche dans
Atlas Loot marquait elle aussi un temps d'arrêt sensible. Cette liste compte environ
20 000 entrées, et sa construction redéterminait le nom et demandait au serveur les
données d'objet pour chacune d'elles - pour 20 000 entrées qu'on n'a même pas encore
consultées. Le nom est déjà connu à cet endroit, et seule l'entrée sur laquelle se
trouve le curseur a besoin des données. La liste s'ouvre donc nettement plus vite.
Technique : alIntegrationQueryAll construit deux index inversés sur les données
d'Atlas Loot : « quel boss fait tomber cet objet » et la liste de noms derrière la
recherche. Le parcours dépendait d'une série de tentatives sur PLAYER_LOGIN
(5/10/20/35/60 s) plus d'un filet PLAYER_ENTERING_WORLD/ZONE_CHANGED_NEW_AREA ; il
se déclenchait donc environ trois secondes après l'entrée dans le monde, sans
découpage, en une seule image. Sa justification était périmée : il devait épargner
au raccourci Ctrl+Maj+L une attente à froid, mais ce raccourci a perdu sa détection
de contexte depuis longtemps et n'ouvre plus que l'entrée de menu, ce qui lance le
calcul de lui-même. Trois des quatre tables remplies par ce parcours n'avaient aucun
lecteur : alLookupBosses et alLookupInstances n'étaient qu'écrites et servaient
uniquement d'indicateur « déjà exécuté ? » pour la série qui l'exécutait ;
alDropsByBoss et alDropsByInstance n'étaient lues que derrière une condition qui ne
peut jamais être vraie, puisque l'indicateur de contexte n'est jamais mis qu'à nil.
Les quatre sont supprimées, avec cet indicateur, son minuteur de 120 s et
BuildContextualWishlistEntry. Ce qui reste tourne en coroutine avec un budget de
30 ms par image : le menu le lance en arrière-plan (QueryAllStart), et ceux qui ont
besoin d'un index complet - recherche, liste de souhaits par donjon, « Lâché par » -
l'attendent à leur propre endroit (alIntegrationQueryAll, sans effet une fois
l'index construit). Chaque ligne coûte aussi moins cher : la résolution du nom
interroge désormais la table d'objets de Sku avant GetItemInfo, qui déclenchait une
requête serveur pour chaque identifiant non mis en cache, et évite les six passes de
substitution pour les noms issus de cette table, qui ne contiennent de toute façon
aucun balisage. La construction du menu de recherche était le second point chaud :
alIntegrationItemMenuBuilder accepte désormais en option le nom que l'appelant
possède déjà (l'index de recherche est justement indexé PAR ce nom), ce qui supprime
par entrée la résolution du nom, la passe Unescape et surtout
SkuCore:RequestItemData - soit un ItemMixin avec son rappel de chargement enregistré
et une requête serveur par identifiant non mis en cache, environ 20 000 fois en une
seule image. La liste de butin d'un boss (quelques dizaines de lignes) continue de
demander les données à l'avance, afin qu'un nom non mis en cache s'y complète tout
seul, et l'entrée sous le curseur les demande de toute façon dans son OnEnter : le
texte prononcé est donc inchangé.
Avertissement de collision : des bips sans obstacle
Simple : le bip « tu es bloqué » pouvait survenir pendant qu'on marchait simplement d'un
PNJ au suivant, alors que rien ne barrait le passage. Le compteur derrière
l'avertissement n'était jamais remis à zéro, si bien que des mesures isolées et
anodines s'accumulaient sur plusieurs minutes et qu'une sur six déclenchait un bip.
Technique : l'avertissement compare la distance parcourue en un tic de 0,15 s à celle que
la vitesse actuelle aurait dû couvrir, et bipe après six tics trop lents. Seulement,
le compteur n'avait aucune remise à zéro en dehors de celle du déclenchement - ni sur
un bon tic, ni quand l'avertissement n'était pas armé : il accumulait donc sur toute
la session au lieu d'exiger six tics d'affilée. Le premier tic après chaque
PLAYER_STARTED_MOVING échantillonne une fenêtre tronquée, puisque la mesure
précédente a été prise à l'arrêt, et compte donc forcément comme « trop lent » :
environ un démarrage de mouvement sur six produisait un bip, à des minutes
d'intervalle et sans le moindre rapport. Cela existe depuis longtemps, mais ne
comptait que tant qu'on tenait soi-même une touche de déplacement ; depuis que les
trajets pilotés par le moteur du jeu sont couverts dans cette version, chaque marche
automatique vers une cible compte aussi - d'où la gêne quand on enchaîne les PNJ. Le
compteur est maintenant remis à zéro à chaque bon tic et dès que l'avertissement
n'est pas armé, exactement comme le faisait déjà la variante « suivi ». Cette
variante reste inchangée : elle est désarmée par AUTOFOLLOW_END, et le son de fin de
suivi provient précisément de ce chemin - l'entendre prouve que l'état a bien été
remis à zéro. L'avertissement laisse en outre une ligne dans le journal de débogage
au déclenchement, pour qu'un signalement de « bips venus de nulle part » puisse être
rattaché au chemin d'où ils viennent.
Vol en taxi : deux annonces de vol qui ne collaient pas
Simple : deux choses étaient annoncées à tort. D'abord, peu avant l'arrivée, Sku
disait « atterrissage anticipé possible à X » - et X était la destination vers
laquelle on volait déjà ; y atterrir « par anticipation », c'est simplement
arriver. Ensuite, « vol terminé » (ou « vol démarré ») arrivait parfois
longtemps après le vol, typiquement au moment où l'on montait ou descendait de
monture, parfois plusieurs minutes plus tard et sans le moindre rapport avec un
point de vol. La première phrase disparaît ; la seconde arrive désormais
exactement quand le vol commence ou se termine réellement, et jamais autrement.
Technique, la destination comme point d'atterrissage : l'exclusion du dernier
point existait bien, mais le curseur d'itinéraire lui glissait dessous au
moment décisif. L'itinéraire est capté au décollage et une escale est
considérée comme franchie dès qu'elle est à moins de 250 yards.
Or le taxi entre droit dans son dernier point : celui-ci était donc « franchi »
lui aussi, le curseur dépassait la fin de l'itinéraire, l'escale suivante était
nulle, et la détermination de la cible retombait silencieusement sur la
solution de secours « point de vol le plus proche ». Cette solution ignore
l'itinéraire : la source passait de « route » à « nearest » - et c'est
précisément ce dont dépend la règle qui rend la dernière étape muette. La
destination était alors largement sous le seuil des 800 yards, d'où l'annonce.
Le curseur s'arrête désormais sur la destination au lieu de la dépasser : la
destination n'est pas une escale que l'on franchit, c'est là où le vol se
termine. La règle tient donc jusqu'à l'atterrissage, et le raccourci peut
toujours nommer la destination à la demande.
Technique, l'annonce tardive : elle reposait sur deux drapeaux que
PLAYER_CONTROL_LOST et PLAYER_CONTROL_GAINED mettaient à 1 et que SEUL le
PLAYER_MOUNT_DISPLAY_CHANGED
suivant remettait à zéro. Si cet événement ne suivait pas - ou arrivait avant
l'événement de contrôle à l'atterrissage - le 1 attendait, tout simplement, et
la monture suivante le consommait. PLAYER_CONTROL_GAINED se déclenche en outre
pour quantité de choses qui ne sont pas un atterrissage (fin d'un contrôle
mental, cinématique, recul, véhicule) : le drapeau pouvait donc être posé sans
le moindre vol. L'état est maintenant DÉDUIT de UnitOnTaxi au lieu d'être
mémorisé : on ne parle que sur une vraie transition faux->vrai / vrai->faux,
chaque événement concerné rejoue le même test idempotent (les événements de
contrôle le rejouent aussi en différé, car le drapeau du client est en retard
sur l'événement), et une connexion ou un /reload adopte l'état courant en
silence.
Technique, par précaution : le module taxi (« atterrissage anticipé possible
à X ») ne pouvait pas échouer ainsi - cette phrase est gardée par UnitOnTaxi
ET par le bouton d'annulation visible. Renforcé tout de même, ces deux
éléments étant de l'état client : reprendre le contrôle signifie désormais
« le vol est terminé, point » - la surveillance est arrêtée et aucun événement
ne peut la relancer avant qu'un nouveau vol soit réellement pris. De plus, un
bouton d'annulation momentanément invisible (un changement de zone en vol
suffit) n'efface plus les verrous d'annonce, ce qui pouvait sinon nommer deux
fois le même point de vol. « /skutaxi » indique le nouvel état sous « landed= ».
Invitation de groupe et invocation : le bouton Refuser est de retour
Simple : lors d'une invitation de groupe ou d'une invocation (« port »), le menu
ne proposait plus que « Accepter » - le bouton pour refuser manquait, et il
manquait aussi longtemps que la fenêtre restait ouverte. Les deux boutons
figurent à nouveau dans la liste.
Technique : Blizzard verrouille le bouton Refuser sur précisément ces demandes
pendant un instant après l'apparition de la fenêtre
(SetupLockOnDeclineButtonAndEscape, la même fonction qui y désactive aussi
Échap), pour éviter un clic malencontreux en cas de spam d'invitations ou
d'invocations. Le lecteur de fenêtres générique de Sku ignore tout élément
grisé, et il analyse une fenêtre contextuelle exactement UNE fois, 0,1 s après
son affichage (GENERIC_OnOpen) - en plein dans cette période de verrouillage.
Le bouton Refuser disparaissait donc, et comme rien ne reconstruit le menu
tant que la fenêtre reste simplement affichée, il ne revenait jamais.
SkuCore:IterateChildren exempte désormais les boutons des dialogues
StaticPopup de la règle « ne jamais lister un élément désactivé » : atteindre
une entrée de menu et appuyer sur Entrée est un acte délibéré, pas un clic
malencontreux. Ces boutons conservent également leur gestionnaire OnClick même
lorsque l'élément désactivé ne signale aucune entrée de clic souris - sinon
l'entrée figurerait dans la liste mais resterait sans effet.
Combat : une fenêtre surgissante pouvait être lue mais pas répondue
Simple : lorsqu'une fenêtre surgissante s'ouvrait pendant un combat - une invitation de
groupe, une invocation, la demande de libération -, Sku pouvait la lire et vous
pouviez y naviguer, mais la touche d'activation ne faisait rien : l'entrée était
simplement annoncée à nouveau. Il fallait attendre la fin du combat, et une
invitation expirait entre-temps. Les boutons des fenêtres surgissantes peuvent
désormais être pressés en combat, les quatre, pas seulement Accepter et Refuser.
Technique : en combat, la touche d'activation n'atteint même pas le bouton de menu
sécurisé : PLAYER_REGEN_DISABLED masque OnSkuOptionsMain, dont le OnHide efface les
liaisons de substitution de ce bouton alors que la fenêtre de grâce est encore
ouverte, et la touche passe alors par le snippet de SkuCombatMenuKey, qui traite
Entrée comme « itinéraire uniquement » et la remet directement au répartiteur non
sécurisé (SkuCore/combatMenuKeys.lua). Lisible, navigable, morte sur Entrée.
Contrairement aux chemins des sacs et de l'échange, cela ne demande ni miroir ni
armement sécurisé : un bouton de StaticPopup n'est PAS un cadre protégé et son
OnClick est du Lua ordinaire (StaticPopup_OnClick appelle les OnAccept/OnCancel du
dialogue : AcceptGroup, DeclineGroup, ResurrectAccept, RepopMe, ConfirmSummon). Aucun
de ceux-ci n'est conditionné à l'événement matériel, l'appel direct fonctionne donc
en combat exactement comme hors combat. Volontairement limité aux boutons de fenêtre
surgissante - toute autre charge de clic de ce constructeur a besoin du véritable
événement matériel et relève du miroir.
Protections : uniquement en combat, uniquement tant que le bouton est bien là, et
uniquement tant que Blizzard le laisse ACTIF (le bouton Refuser est verrouillé pendant
sa première seconde, où « /click » est tout aussi inopérant). Nouveauté également :
les quatre boutons sont traités ainsi ; les boutons 3 et 4 retombaient auparavant sur
le chemin de clic générique sans aucune action non sécurisée. /skucheck menu compte
les cas où une activation en combat n'a plus trouvé de bouton à cliquer.
Saisie dans les champs de texte : plus de lettres qui continuent après la fermeture
Simple : en tapant dans un champ de saisie de Sku (écrire un courrier, un champ de
recherche, l'hôtel des ventes), l'annonce des caractères prenait du retard, se
bousculait, et des lettres arrivaient encore alors que le champ était fermé
depuis longtemps - surtout avec les voix SAPI, qu'une frappe n'interrompait
pas. Sku se comporte désormais comme un lecteur d'écran : la frappe suivante
interrompt l'annonce de la précédente au lieu de se mettre à la file. Ce qui a
été tapé pendant une annonce en cours est regroupé en UNE seule annonce, si
bien qu'un collage avec Ctrl+V ou une touche fléchée maintenue ne provoque plus
de déluge. Et à la fermeture du champ - par Entrée, par OK, par Échap ou par
tout autre chemin - Sku ne se contente plus de jeter ce qui attend : il
interrompt aussi ce qui est en train d'être prononcé. Corrigé au passage : les
caractères ( et % n'étaient jamais annoncés pendant la saisie.
Technique : l'écho de SkuOptions:EditBoxShow ajoutait un énoncé par caractère tapé
à la file BTTS. Le plafond introduit en v42.11 (GetBttsQueueDepth /
TrimBttsQueue) ne pouvait pas fonctionner : la pompe de SkuVoice saute sa
cadence de 0,1 s dès que plus d'une entrée attend (`#mSkuVoiceQueueBTTS > 1`)
et transmet toute la rafale à C_VoiceChat.SpeakText en quelques images, donc
dans la file du CLIENT. La file propre de Sku est ensuite vide, la profondeur
mesurée est quasiment toujours 0, le plafond ne se déclenche jamais, et à la
fermeture Trim ne trouve plus rien à jeter - à ce moment-là le retard est dans
la file client/SAPI, que seul un vrai StopSpeakingText peut atteindre. L'écho
passe maintenant par un seul emplacement : les caractères sont accumulés
jusqu'au prochain vidage (6 au maximum, les plus récents gagnent), prononcés en
UN énoncé en mode écrasement, avec un intervalle minimal de 0,1 s ; un compteur
de génération annule les minuteries de vidage et les lectures de flèches
différées dès que le champ est fermé. Nouveau : SkuVoice:CancelBttsOutput -
vide la file BTTS, appelle StopSpeakingText et arme le délai d'attente de la
pompe à 0,15 s pour que la confirmation émise juste après ne soit pas emportée
par l'arrêt asynchrone ; il est branché sur ENTRÉE, OK, ÉCHAP et sur le OnHide
du cadre (un :Hide() extérieur est donc couvert aussi). Également :
SkuVoice:CheckIgnore utilise désormais string.find(..., plain=true) - une
ponctuation tapée comme ( ou % était interprétée comme un motif Lua et levait
« unfinished capture » / « malformed pattern », erreur avalée par le pcall de
l'appelant ; et l'écho passe ignoreLinks, car chaque frappe traversait
auparavant toute la recherche de liens wiki dont le résultat est ici ignoré.
Améliorations de performance et corrections lors du chargement de la base de sorts
Simple : À la connexion, certains joueurs entendaient « Sku erreur de base de données,
jeu de données spells ». Le problème a été signalé depuis Era, mais le facteur
commun est la puissance de la machine et non le client : sur une machine rapide
cela n'arrivait jamais, sur une machine plus faible à chaque fois, et TBC aurait
tout aussi bien pu être touché. La base elle-même n'a
jamais été endommagée : seule échouait la liste de sélection des auras qui en est
dérivée, construite en UNE seule passe sur environ 90 000 entrées. Sur une machine
lente, le client interrompait cette passe avec « script ran too long » - et comme
la liste était écrite au fur et à mesure, elle restait ensuite à moitié remplie :
une partie des sorts manquait dans les réglages d'auras, et les auras pointant sur
une entrée manquante n'annonçaient plus rien ou laissaient le menu sur une erreur.
La construction se fait maintenant par petites tranches réparties sur plusieurs
images, exactement comme le reste de la construction de la base après la
connexion, et n'est publiée d'un seul bloc qu'à la toute fin. Si elle est tout de
même interrompue, les listes précédentes restent intactes au lieu d'être à moitié
remplies, aucune erreur de base de données n'est prononcée, et le reste des
données de sorts reste considéré comme valide. De plus, la liste n'est construite
qu'une fois par session au lieu de l'être à chaque écran de chargement.
Technique : SkuAuras:BuildAttributeValueLists parcourt itemLookup ET SpellDataTBC et
alloue une table plus une ou deux chaînes concaténées par ligne. Elle était
appelée de façon synchrone depuis deux endroits : l'étape de construction
« auraValueLists » (après items+spells) et PLAYER_ENTERING_WORLD. La fonction
accepte désormais un callback de yield facultatif, consulté toutes les 1000
lignes, construit dans des tables locales et les publie à la fin par une
affectation chacune (atomique - une interruption ne change plus rien). Elle est
pilotée par sa propre coroutine avec une pompe OnUpdate qui partage le budget
d'images d'après-connexion (Sku:BuildFrameBudgetMs, enregistrée comme worker
« skuAuraLists ») : trois workers actifs coûtent toujours 150 ms par image au
total. L'étape de construction ne fait que démarrer ce pilote et n'appelle plus
ctx.fail : l'échec de la liste dérivée marquait auparavant toute la famille de
données « spells » comme échouée et déclenchait l'annonce d'erreur, alors que les
données de sorts étaient complètes. Les erreurs vont maintenant dans SkuErrorLog
(« skuAuraLists ») et dans le journal de débogage. PLAYER_ENTERING_WORLD démarre
le même pilote, qui ne fait rien une fois la construction réussie - la
reconstruction complète à chaque changement de zone et à chaque entrée en instance
disparaît donc. La construction ignore également une ligne de sort sans nom pour
la langue active au lieu de s'y arrêter, et les trois accès friendlyName non
protégés du menu des auras retombent sur la clé brute.
Un ennemi qui vous a empoisonné avant de mourir n'est plus recompté
Simple : L'annonce du nombre d'ennemis est fiable dans les combats ordinaires depuis
la v42.10, mais il restait un cas : si un ennemi vous infligeait un poison - ou
tout autre effet persistant - juste avant de mourir, le nombre descendait
correctement à sa mort, remontait d'un cran un instant plus tard, et ne
redescendait définitivement qu'à l'expiration du poison, bien après la mort de
l'ennemi. C'est corrigé, et sans toucher à la détection elle-même : rien de
nouveau n'est compté, une seule chose cesse d'être comptée à tort.
Technique : Un ennemi mort est mémorisé par son GUID afin qu'une ligne de journal de
combat tardive ne puisse pas le ressusciter. Cette marque était jusqu'ici effacée
en bloc dans PLAYER_REGEN_ENABLED - c'est-à-dire exactement au moment où la mort
du dernier ennemi vous sort du combat, donc juste avant l'arrivée du premier tic
de poison. Chaque tic est une ligne de journal de combat avec l'ennemi mort comme
source et vous comme cible ; la logique d'ajout y lit « un ennemi nous combat »
et réadmettait le cadavre par son GUID. Le deuxième zéro ne venait plus alors que
du balayage d'inactivité de 6 secondes après le dernier tic. La marque porte
désormais un horodatage et n'est plus effacée en bloc à la fin du combat, mais
seulement lorsqu'elle a plus de 60 secondes. Rien ne change à l'intérieur d'un
combat - la marque y vivait de toute façon jusqu'à la fin du combat, et c'est
pourquoi le cas « l'un de deux ennemis meurt empoisonné pendant que le second
continue » était déjà compté correctement. Aucun nouvel état ni aucune nouvelle
règle particulière n'est ajouté, seulement une durée de vie pour une marque qui
existait déjà. La soupape existante demeure : si le même GUID réapparaît
manifestement vivant sur un jeton d'unité, la marque est levée immédiatement, un
vrai ennemi ne peut donc jamais disparaître durablement du décompte.
Un échange conclu avec succès était annoncé comme une confirmation retirée
Simple : Quand un échange aboutissait vraiment, il se terminait malgré tout sur
« a retiré sa confirmation ». Rien n'avait échoué : à la conclusion,
le serveur enlève d'abord les deux coches de confirmation et ne ferme la fenêtre
qu'ensuite, et cet effacement ressemble exactement à un vrai retrait. La fausse
ligne disparaît. Aucune annonce « échange terminé » ne la remplace, et c'est
délibéré : le client 2.5.6 ne fournit aucun signal distinguant la conclusion de
l'annulation, et un message deviné serait pire que pas de message du tout.
Technique : Capturé pour un enchantement réussi dans un échange, le tout en une
seconde : TRADE_ACCEPT_UPDATE(1,1) -> (0,1) -> (0,0) -> TRADE_CLOSED.
TRADE_ACCEPT_UPDATE lisait ces transitions 1->0 comme un vrai retrait. Impossible
de les distinguer : TRADE_CLOSED ne transporte aucune donnée, et
ERR_TRADE_COMPLETE / ERR_TRADE_CANCELLED n'existent pas sur 2.5.6 (vérifié contre
le TradeInfoDocumentation.lua livré avec le client). Les deux annonces de retrait
attendent maintenant 0,5 s derrière un compteur de génération que
ResetTradeAcceptState() incrémente à chaque frontière d'échange, et sont
abandonnées si l'échange est terminé, si la fenêtre a disparu ou si le côté
concerné est repassé à confirmé.
« Échange confirmé » était avalé à chaque fois
Simple : Si vous confirmiez l'échange depuis le menu, vous n'entendiez jamais votre
propre confirmation. Et sur plusieurs annonces d'échange rapprochées, seule la
dernière était prononcée - donc justement la mauvaise. Elles arrivent maintenant
toutes, dans le bon ordre.
Technique : OutputStringBTtts avec aOverwrite=true ajoute un « queuereset », et la
pompe supprime tout ce qui est en file AVANT ce marqueur ; dans une rafale,
chaque annonce d'échange dévorait donc la précédente. Toutes les annonces
d'échange passent désormais false. Cela ne sauve pas encore votre propre
confirmation : lorsque vous acceptez depuis le menu, la réponse du serveur est
déjà en file avant que le menu n'ajoute sa propre ligne pour l'appui de touche,
dont la réinitialisation la supprime alors. Les annonces de confirmation passent
donc par tSayAccept, avec un délai fixe de 0,35 s qui les place derrière la ligne
du menu. De l'ordonnancement de file uniquement, aucune déduction d'état - et
volontairement sans contrôle de génération, car un échange qui se ferme juste
après est précisément le cas où cette confirmation compte le plus.
Entrée désenchante ou enchante à nouveau un objet du sac, dans les deux ordres
Simple : Si vous lanciez Désenchantement (ou preniez un enchantement, une pièce
d'armure, une huile d'arme) alors que vous étiez déjà sur l'objet cible dans la
liste des sacs, puis appuyiez sur Entrée, il ne se passait rien du tout - seul
Ctrl+Entrée fonctionnait. L'ordre inverse marchait : lancer d'abord, puis
naviguer jusqu'à l'objet. Entrée applique maintenant dans les deux ordres. Le
clic gauche est bien la voie prévue par le jeu : dans le code des sacs de
Blizzard, un clic gauche sur un objet l'utilise pour le sort tant qu'un sort
attend une cible objet. En v41 cela passait par l'ancienne entrée « clic
gauche » ; la refonte du menu en v42.00 l'a perdue, et la v42.04 n'a rétabli
l'application que pour l'ordre « lancer d'abord ».
Technique : Le bouton sécurisé du clic gauche est garni au moment où une entrée est
FOCALISÉE, et la macro d'application d'un objet de sac (« /use sac
emplacement ») uniquement tant que SpellIsTargeting() est vrai à cet instant. Si
le sort est lancé après, le bouton gardait la garniture d'avant - pour un objet
de sac, donc aucune -, et l'instantané de ciblage pris dans PreClick fait en
outre renoncer le repli non sécurisé PickupContainerItem : l'appui de touche ne
faisait littéralement rien. La garniture a quitté le OnEnter générique pour
SkuOptions:StageClickMacros (les deux boutons sécurisés) et est réexécutée depuis
deux endroits : CURRENT_SPELL_CAST_CHANGED, dont le gestionnaire était vide et
dont la signature de répartiteur était décalée d'un cran, et le PreClick du
bouton gauche lui-même, qui s'exécute avant que le gestionnaire sécurisé ne lise
les attributs - l'échange compte donc encore pour cet appui précis. Seules les
entrées porteuses d'une macro d'application sont regarnies, uniquement menu
ouvert et hors combat.
Ctrl+Entrée sur un objet de marchand ouvrait la cabine d'essayage au lieu d'acheter
Simple : Chez un marchand, Ctrl+Entrée (clic droit) sur un objet doit en acheter
un. À la place, la cabine d'essayage s'ouvrait. L'achat par le sous-menu de
l'objet a toujours fonctionné et ne change pas ; la touche achète désormais
aussi, y compris dans l'onglet de rachat.
Technique : La touche du clic droit porte Ctrl par défaut, une macro « /click
RightButton » lit l'état CLAVIER EN DIRECT, et le XML de chaque bouton
d'objet natif redirige TOUT clic modifié vers *_OnModifiedClick ->
HandleModifiedItemClick -> DressUpLink : la branche du clic droit simple n'est
jamais atteinte. C'est le même piège qui avait cassé les huiles sur les armes
équipées, résolu là par « /use ». Les objets de marchand appellent
maintenant directement le MerchantItemButton_OnClick non enveloppé de Blizzard,
ce qui conserve les confirmations de coût spécial et de prix élevé. Les sacs, la
banque et les emplacements d'équipement étaient déjà immunisés car ils passent
par « /use » ou l'API des conteneurs. Partout ailleurs où Sku clique un bouton
natif (butin, échange, courrier, grimoire, banque de guilde), la touche du clic
droit n'a pas d'action propre à perdre : Ctrl+Entrée n'y fait rien ou affiche la
cabine d'essayage, tandis qu'Entrée déclenche la vraie action.
Le clic gauche peut désormais être assigné à une touche avec Maj, Ctrl ou Alt
Simple : La touche d'activation peut être réassignée à une combinaison comme
Ctrl+Entrée et fonctionne quand même. Auparavant, une telle touche aurait
ouvert la cabine d'essayage chez un marchand ou sur un objet équipé au lieu
d'agir, pour la même raison que le clic droit chez le marchand.
Technique : Les entrées dont le clic serait avalé par un modificateur portent
maintenant une seconde charge, insensible aux modificateurs (plainMacrotext),
qui appelle l'action directement au lieu de cliquer le bouton natif : objets de
marchand via le MerchantItemButton_OnClick non enveloppé de Blizzard,
emplacements d'équipement via PickupInventoryItem, emplacements d'échange via
ClickTradeButton / ClickTargetTradeButton. Elle n'est mise en place que tant
qu'un modificateur est réellement enfoncé - la décision se prend dans le
PreClick du bouton sécurisé, seul moment où l'état du clavier est lisible, et
qui s'exécute avant la lecture des attributs. Avec la touche Entrée par défaut,
sans modificateur, la mise en place reste identique à l'octet près : aucune
régression possible de ce côté. Les sacs, la banque, les récompenses de quête,
la banque de guilde, les fenêtres de confirmation et les onglets n'avaient
besoin de rien : ils passent par l'API des conteneurs ou n'ont aucune branche
de clic modifié. Les boutons de butin, les pièces jointes du courrier et les
boutons du grimoire ont bien cette branche, mais Sku ne les clique jamais - le
butin a son propre traitement, les pièces jointes passent par TakeInboxItem, et
le grimoire ne fait pas partie des fenêtres ouvertes par Sku. S'il venait à
l'être, il lui faudrait « /cast » plutôt que ce mécanisme, car
l'incantation est une fonction protégée qu'aucun appel direct ne peut déclencher.
Les touches de clic du menu ne pouvaient pas être assignées du tout
Simple : Dans les raccourcis, « Menu ; clic gauche/activer » et « Menu ; clic
droit » répondaient « Invalide. Appuyez sur une autre touche. » à chaque
Entrée pressée - avec ou sans modificateur - et ne pouvaient donc pas être
réglées, pas même remises à leur propre valeur par défaut. Les deux sont
assignables désormais.
Technique : La liste des touches réservées est comparée en SOUS-CHAÎNE : « ENTER »
attrape donc aussi NUMPADENTER et CTRL-ENTER. Elle protège les touches de
navigation du menu - mais ces deux assignations vivent précisément sur cette
famille : Entrée est le défaut de l'une, Ctrl+Entrée celui de l'autre. Elles en
sont maintenant exemptées, uniquement pour les touches Entrée ; les flèches, le
retour arrière et la tabulation y restent bloqués, car ils pilotent la
navigation, qui ne fait pas partie de cette assignation. Les touches du menu de
combat disposaient déjà d'une telle exemption. Appliqué aux deux endroits qui
capturent une touche : le menu des raccourcis et l'assistant d'entrée unique.
La touche de clic du menu n'efface plus l'assignation de jeu de la même touche
Simple : Remettre « Menu ; clic gauche/activer » sur Entrée avertissait que la
touche était « déjà assignée à Ouvrir la discussion » puis, après
confirmation, faisait exactement cela : Entrée n'ouvrait plus la discussion.
Impossible de revenir en arrière non plus, car pour les commandes du jeu le
menu des raccourcis refusait toute touche Entrée comme invalide. Or ces deux
assignations ne se sont jamais gênées - elles partageaient Entrée depuis des
années : tant que le menu est ouvert, Entrée active l'entrée, sinon elle ouvre
la discussion. C'est de nouveau le cas. Quand une touche est ainsi partagée,
Sku l'annonce désormais (« Remarque ! Cette touche est également utilisée
par ... Les deux assignations sont conservées. ») au lieu de supprimer l'autre
assignation. Si la discussion vous manque déjà : réassignez simplement
« Ouvrir la discussion » à Entrée dans le menu des raccourcis, c'est possible
maintenant. Le reste du comportement en cas de conflit ne change pas - deux
touches Sku sur la même touche sont toujours signalées et l'ancienne libérée.
Technique : Les touches de clic du menu ne sont jamais une véritable assignation
mais une liaison de substitution sur le bouton sécurisé du menu, armée par son
OnShow et effacée par son OnHide : elle n'existe que tant que le menu est
ouvert et prime sur la commande du jeu pendant ce laps de temps. Il en va de
même pour les touches du menu de combat, armées uniquement pendant un combat.
La vérification des conflits ignorait cette différence : elle compare des noms
de touches bruts, signalait donc un conflit et, à la confirmation, appelait
SetBinding(touche) puis SaveBindings - ce qui retirait définitivement OPENCHAT
d'Entrée. Cela n'est devenu atteignable que parce que le correctif ci-dessus a
rendu ces assignations possibles sur Entrée. Les entrées concernées portent
désormais un marqueur transientOverride dans skuDefaultKeyBindings, lu via
SkuOptions:SkuKeyBindsIsTransientOverride ; pour elles, la gestion de conflit
contre les COMMANDES DU JEU est abandonnée (pas de confirmation, pas de
libération) et remplacée par la remarque vocale. Contre d'autres assignations
Sku, elle reste entièrement en place, car celles-ci sont armées en même temps
et se chassent réellement. La même règle s'applique dans l'autre sens afin
qu'une commande du jeu ne libère pas la touche du menu, et la famille Entrée
n'est plus bloquée lors de l'assignation des commandes du jeu - les flèches, le
retour arrière et la tabulation le restent. La remarque est accrochée à la même
annonce que la nouvelle touche, car un appel séparé avec aOverwrite vide la
file d'attente et se serait avalé lui-même.
Les objets de la banque portent de nouveau leur nom
Simple : À la banque - et occasionnellement ailleurs - certains objets étaient
annoncés avec le texte d'attente du client (« Retrieving item information » en
anglais) au lieu de leur nom. Ce n'est pas un message d'erreur de l'addon mais
un texte d'attente du jeu : le
client l'affiche tant qu'il récupère encore les données d'un objet auprès du
serveur. Étaient surtout touchés les objets à infobulle volumineuse - recettes,
pièces d'ensemble, équipement serti -, car leur affichage n'est complet qu'une
fois TOUTES les données qui y sont mentionnées arrivées. Jusqu'ici Sku reprenait
ce texte d'attente comme NOM de l'entrée, et n'en démordait plus : il ne
disparaissait pas, ni en revenant sur l'entrée, ni en quittant puis rouvrant le
menu. Doublement gênant, car la phrase est identique pour chaque emplacement
concerné - plusieurs objets en attente sonnaient pareil, impossible de les
distinguer ni de viser l'un d'eux, et c'est aussi ce que comparaient le tri par
nom et le saut par première lettre. Désormais Sku prend le nom dans sa propre
liste d'objets fournie avec l'addon quand le client ne l'a pas encore, n'ajoute
qu'un bref « (chargement) », demande activement les données au serveur, et relit
l'entrée dès qu'elles arrivent. À l'ouverture de la banque, les données de tous
les emplacements sont en outre demandées à l'avance, de sorte que le premier
passage n'attend généralement pas du tout.
Technique : La phrase est la constante du jeu RETRIEVING_ITEM_INFO. Il n'existait pas
une seule vérification contre elle dans l'addon - la seule mention dans le code
se trouvait dans un outil de développement et comparait un texte allemand écrit
en dur. L'état restait permanent pour trois raisons : aucun récepteur de
GET_ITEM_INFO_RECEIVED nulle part, aucune demande de chargement (écrire dans une
infobulle n'est pas une requête que l'on peut suivre), et aucune seconde voie
vers un nom. Nouveautés : SkuUtil:IsRetrievingItemInfo (détecte le texte
d'attente via la constante du jeu plutôt que via du texte allemand),
SkuCore:ResolveItemName (cache du client, puis SkuDB.itemLookup, puis le nom
dans le lien), SkuCore:PendingItemLabel, SkuCore:RequestItemData et
SkuCore:ContinueOnItemData via Item:CreateFromItemID():ContinueOnItemLoad. Un
cadre d'événements dédié collecte GET_ITEM_INFO_RECEIVED et en déclenche une
seule reconstruction silencieuse groupée ; BANKFRAME_OPENED demande à l'avance le
conteneur -1 et les sacs de banque 5 à 11. Si la banque était le point noir, il y
a une raison précise : SetBagItem(-1, emplacement) ne renvoie rien sur 2.5.6, si
bien que la refonte des sacs s'y rabattait sur
SetHyperlink(GetContainerItemLink(...)) - précisément la voie qui dépend du cache
d'objets. Les emplacements de banque sont de vrais emplacements d'équipement
(emplacement n = emplacement d'équipement n + 39), donc SetInventoryItem les lit
aussi localement que SetBagItem lit un sac ; ce n'est pas un retour aux éléments
de fenêtre lus que la refonte a supprimés - pas de cadre, pas de OnEnter, pas
d'ouverture forcée des sacs. Aucune voie de repli n'a été laissée derrière,
volontairement : elle ne ferait que masquer si la bonne voie fonctionne.
Les objets ne disparaissent plus des listes de butin d'AtlasLoot
Simple : Des entrées manquaient dans les listes de butin - sans trou, sans
indication. Une liste de douze butins possibles en affichait sept et se lisait
comme une liste complète de sept. La cause est la même qu'au point précédent :
les objets dont le client n'avait pas encore reçu les données du serveur
n'étaient pas reconnus comme des objets et étaient purement et simplement omis.
Cela touchait justement les entrées qui font l'intérêt de la fonction, car une
liste de butin est presque uniquement composée d'objets que l'on n'a jamais
ramassés ni consultés - moins le boss est familier, plus il en manquait. Ce
n'était pas non plus reproductible : ouvrir la même liste une seconde fois
pouvait en donner une plus longue, avec des numéros d'entrée décalés. Désormais
toutes les entrées sont là, et la recherche par nom trouve aussi les objets que
l'on n'a jamais eus en main. Les listes deviennent donc plus longues que
d'habitude, et les numéros d'entrée se décalent une fois en conséquence.
Technique : À quatre endroits, un aiguillage décidait à partir de
C_Item.GetItemNameByID(id) si une ligne de butin est un objet ou un sort - un
test qui confond « pas un objet » et « pas encore envoyé par le serveur ». Un
objet non chargé tombait dans la branche des sorts, GetSpellInfo ne renvoyait
rien pour lui, et la ligne était écartée. L'intention du test est juste (garder
les identifiants aberrants hors du menu), seule la question était mal posée :
GetItemInfoInstant y répond localement depuis la base d'objets du client. Il
existe maintenant l'assistant tIsItemId pour cela ; à l'un des quatre endroits,
le problème avait déjà été contourné isolément par un ajout SkuDB, que ceci
remplace. L'ordre des branches est inchangé. La sortie anticipée de la branche
« item » du constructeur de menu disparaît également ; l'entrée est nommée via
SkuCore:ResolveItemName ou - de façon stable - « objet »,
volontairement pas la forme « (chargement) », qui déplacerait l'entrée sous le
curseur une fois les données arrivées. tItemNameTable, derrière la recherche par
nom, est également remplie via ResolveItemName, et l'appel GetItemNameByID sur
les favoris à la connexion, dont le résultat était jeté, est désormais une vraie
demande de chargement.
Le navigateur de donjons proposait tous les donjons au lieu des seuls accessibles
Simple : sous « Créer une entrée » figurait la liste complète des donjons de la
catégorie - la même à tous les niveaux. Un personnage de niveau vingt se voyait
proposer les instances héroïques de l'Outreterre, un niveau soixante-dix les
Mines de Sombrefer, et les deux pouvaient être cochés et publiés alors qu'on ne
peut pas y entrer. La fenêtre visible n'a jamais fonctionné ainsi : le jeu y
restreint la liste à ce qui correspond à votre niveau, et c'est précisément cette
restriction qui manquait chez nous - nous lisions simplement la liste non filtrée.
Sku affiche désormais la même sélection que la fenêtre. Si vous avez malgré tout
besoin de la liste entière, par exemple pour vous proposer sur un donjon très
inférieur à votre niveau, désactivez la nouvelle entrée « Donjons adaptés
uniquement » ; elle se trouve juste au-dessus des groupes de donjons et indique,
lorsqu'elle est active, combien d'entrées elle masque. Les donjons déjà cochés
restent toujours visibles même si la restriction devait les masquer - sinon vous
publieriez quelque chose que vous ne retrouveriez plus dans le menu.
Technique : tGetCategoryActivities appelait
C_LFGList.GetAvailableActivities(categoryID) sans l'argument filters et obtenait
donc toute la catégorie ; les niveaux issus de GetActivityInfoTable n'étaient lus
que pour l'intitulé, jamais pour filtrer. Le filtrage se fait maintenant en deux
couches combinées par ET : d'une part via l'ensemble du jeu lui-même
(GetAvailableActivities(categoryID, nil, Enum.LFGListFilter.Recommended)), d'autre
part localement face à UnitLevel("player") au moyen de minLevelSuggestion/
maxLevelSuggestion - sur 2.5.x ce sont les seuls champs portant de vrais niveaux.
L'ensemble du jeu n'est retenu que s'il revient non vide ET comme un véritable
sous-ensemble de la liste non filtrée, afin qu'une signature divergente ne puisse
jamais vider le menu ; une donnée de niveau absente laisse passer l'entrée.
Nouveautés : le réglage dungeonBrowser.showAllActivities (char), la bascule
SkuCoreDungeonToggleShowAll (qui reconstruit l'onglet, la liste des enfants
changeant), les compteurs de DungeonBrowser.tFilterStats et une ligne dprint
« activity filter » portant les décomptes individuels des deux couches.
Menu rapide : erreurs Lua sur certains réglages
Simple : dans le menu rapide, plusieurs réglages déclenchaient une erreur Lua dès
qu'on les ouvrait avec la flèche droite - surtout sous « Synthèse vocale », mais
aussi le volume de la balise sous « Volumes » et les interrupteurs oui/non sous
« Divers ». Les mêmes réglages atteints par leur chemin normal dans le menu des
réglages fonctionnaient parfaitement. C'est que le menu rapide ne possède aucun
réglage propre : il affiche les mêmes entrées une seconde fois à un second
endroit, et cet affichage secondaire omettait une information sans laquelle Sku
ne retrouvait pas la valeur enregistrée. Les deux chemins affichent désormais la
même valeur et écrivent dans le même réglage. De plus, « Divers » présente à
nouveau les quatre entrées prévues : « Ne pas masquer l'infobulle » et « Jouer
les salutations des PNJ » en avaient discrètement disparu lorsqu'elles ont reçu
une nouvelle place ailleurs dans le menu des réglages.
Technique : SkuOptions:IterateOptionsArgs décide à partir de son quatrième argument
(aModule) si un nœud est géré par le schéma : skuManaged = (v.get == nil and
v.set == nil and aModule ~= nil). Sinon, GetCurrentValue lit via
self.optionsPath[self.profileIndex]:get() - or ces closures get/set sont
précisément ce que les nœuds gérés par le schéma n'ont pas, puisqu'ils vivent
d'aModule. Les six appels miroir du menu rapide dans SkuZOptions/Core.lua ne
transmettaient pas aModule : ouvrir un tel nœud tombait donc droit sur
« attempt to call method 'get' (a nil value) ». Étaient touchés
WowTtsVoice/-Speed/-Volume (SkuChat), beaconVolume (SkuNav),
readAllTooltips/interactMove (SkuCore) et vocalizeMenuNumbers/vocalizeSubmenus
(SkuOptions) ; les canaux audio et les réglages de son restaient intacts car ils
portent leurs propres get/set. Les six appels transmettent maintenant module et
keyPrefix exactement comme l'emplacement de menu d'origine (« SkuOptions »/
« soundChannels. », « SkuNav »/« », « SkuOptions »/« soundSettings. »,
« SkuChat »/« », « SkuCore »/« », « SkuOptions »/« »), de sorte que les valeurs
enregistrées restent inchangées. L'appel de « Divers » reçoit en plus
aIncludeHidden = true, car doNotHideTooltip et playNPCGreetings portent
désormais forAudioMenu = false et étaient filtrés sans un mot. Par ailleurs,
tous les nœuds produits par IterateOptionsArgs lisent et écrivent maintenant via
deux aides centrales (tOptGet/tOptSet) à trois niveaux - SkuSettings, closure
get/set propre, accès direct à la table -, si bien qu'un futur emplacement
miroir sans aModule lira au pire la valeur directement au lieu de lever une
erreur.
Deux déplacements dans le menu : les réglages de son des balises et « Divers »
Simple : Les réglages de balise (volume de balise, clic sur les balises avec son
angle et son son, ainsi que les jeux de sons pour les balises étroites, larges
et la dernière) se trouvaient jusqu'ici sous Moniteur. Ils sont désormais là où
se trouvent tous les autres réglages de navigation : Réglages, Navigation,
Réglages de son des balises. L'entrée ne s'appelle donc plus simplement
« Balise » mais dit de quoi il s'agit, et Moniteur ne la contient plus du tout.
Par ailleurs, « Divers » est de nouveau la toute dernière entrée du menu
rapide - « Dial Targeting » et « Soft Targeting » se placent maintenant avant,
et non plus après.
Technique : Le bloc balise passe tel quel de Aq:MonitorMenuBuilder (SkuCore/aq.lua)
dans la branche Navigation de SkuCore:MenuBuilder (SkuCore/Options.lua), où il
est accroché comme sous-entrée « Réglages de son des balises » (en allemand
« Beacon Soundeinstellungen », en anglais « Beacon sound settings ») sous les
réglages de navigation. Ce sont les mêmes nœuds d'options issus de
SkuNav.options.args, transmis via IterateOptionsArgs vers
SkuSettings:Sub(« SkuNav ») avec le même module et le même keyPrefix - les
valeurs enregistrées restent donc inchangées, et les nœuds de jeux de sons
conservent leur OnAction qui joue une balise d'exemple. Comme avant, la liste
est une sélection explicite et non toute la table args : un nouveau nœud de
balise doit y être ajouté aussi. Dans le menu rapide (SkuZOptions/Core.lua),
Dial Targeting et Soft Targeting ont été déplacés avant le bloc 7.4 ; cette
liste d'enfants ne porte pas d'indicateur sorting, l'ordre d'insertion est donc
l'ordre d'affichage.
-------------------------------------------------------------------------------------------------------
Changements dans Sku v42.12
La feuille de personnage relit la description de chaque caractéristique
Simple : Sous « Stats », chaque valeur - dégâts des sorts, score de toucher des
sorts, endurance, vitesse d'arme et toutes les autres - n'avait que son nom et
son chiffre. La description que le jeu affiche à côté était vide : ce que fait
la caractéristique, le détail par école de magie, main droite et main gauche.
La touche de texte intégral ne disait rien sur ces entrées. Seules les
résistances et votre équipement avaient une description. Désormais chaque
caractéristique en a une, et elle est relue chaque fois que vous arrivez sur
l'entrée, elle est donc à jour après un buff ou un changement d'équipement
également.
Technique : La lecture de l'infobulle était mise en commentaire à cet endroit et
chaque entrée était construite avec un textFull vide - inchangé depuis la
v41.06, ce n'est donc pas la refonte qui l'a cassé, cela n'a jamais fonctionné.
Deux raisons pour lesquelles cela ne pouvait pas fonctionner tel qu'écrit :
GetButtonTooltipLines était appelé avec GameTooltip comme deuxième argument, ce
qui saute précisément l'étape qui remplit l'infobulle ; et sans cet argument
l'assistant aurait quand même abandonné, car il ne déclenche un OnEnter que pour
les objets portant un marqueur .type - les cadres de caractéristiques n'en ont
pas, ce qui est exactement pourquoi la boucle des résistances le pose avant sa
propre lecture. S'y ajoutait une troisième raison, celle qui aurait rendu un
correctif naïf faux : TBC fait passer les 29 caractéristiques par UN SEUL widget
partagé (PlayerStatFrameLeft1), et les setters de Blizzard y laissent leur état -
.tooltip/.tooltip2 ainsi que .bonusDamage/.spellCrit/.damage pour les quatre
caractéristiques qui installent leur propre OnEnter. Aucun d'eux n'efface ce que
le précédent a laissé, quatre d'entre eux remplacent OnEnter sans jamais le
remettre ; le propre UpdatePaperdollStats de Blizzard le repointe avant chaque
groupe pour exactement cette raison. Sans la même remise à zéro, la
caractéristique N aurait lu la description de la caractéristique N-1. La lecture
réinitialise maintenant le widget avant chaque setter et parcourt l'infobulle via
NumLines/GameTooltipTextLeft|Right plutôt que GetRegions - ce dernier rend la
colonne de gauche et celle de droite comme deux séries distinctes, si bien que le
détail par école de magie aurait été lu comme six noms d'école suivis de six
chiffres. Le getter dynamique qui relit la valeur à chaque arrivée renvoie
désormais la description comme deuxième valeur à côté d'elle, et le widget est
ensuite rendu à l'état que Blizzard attend via PaperDollFrame_UpdateStats.
Sku ne dépend plus du fait que des jetons internes restent non traduits
Simple : Sur un client français, la touche du journal de quêtes ne faisait
absolument rien - pas de focus, pas de son, pas d'erreur. Un seul mot traduit
dans le fichier de localisation française en était la cause. Certaines entrées
de ce fichier ne sont pas un libellé mais un jeton interne que Sku utilise pour
construire et reconnaître ses propres chemins de menu ; traduisez-en un et
l'addon ne retrouve plus son propre chemin. C'est corrigé, et corrigé d'une
manière qui ne peut pas se reproduire : le jeton n'est plus un texte
traduisible. Si vous avez pris l'habitude de taper un chemin en français, il
vous mène toujours au bon endroit. Une deuxième trouvaille du même genre aurait
fait basculer silencieusement tous les clients français vers les noms anglais
des créatures, des objets et des quêtes à la prochaine remise en ordre du
fichier de localisation - cela aussi a disparu.
Technique : Trouvé, diagnostiqué et corrigé par ZenqFR (Games-Access) sur un vrai
client français, sous la forme de la PR #2 de ce dépôt. Les modifications qui
suivent s'appuient dessus. L["short"] portait deux sens sans rapport : dans
environ 25 endroits c'était le premier champ d'un chemin SkuOptions:SlashFunc,
c'est-à-dire un jeton de protocole que le code compare avec lui-même, et dans
exactement un endroit (SkuCore/aq.lua) c'est un véritable texte destiné à
l'utilisateur. Un traducteur ne voit jamais que le second sens. Les deux sont
désormais séparés : Sku.MENU_ROOT est le jeton, utilisé par les 23 endroits qui
construisent des chemins ainsi que par les six qui codaient en dur le littéral
"short," - dont la touche du journal de quêtes. La comparaison accepte toujours
L["short"] également, de sorte qu'un chemin localisé que quelqu'un a appris
continue de fonctionner. Corrigé au passage : le repli dans
SkuCore/dungeonBrowser.lua était "sku", ce qui n'était pas un jeton valide du
tout. Deuxièmement, frFR.lua définissait L["locale"] deux fois - "enUS" dans le
corps trié et "frFR" ajouté à la fin. Cela ne se résolvait correctement que
grâce à l'ordre d'AceLocale : une locale non par défaut est enregistrée via un
proxy qui fait un rawset inconditionnel, donc la DERNIÈRE affectation gagne.
Sku.Loc est lu directement depuis cette clé, et elle détermine quelles tables de
noms SkuDB tout le client utilise - régénérer ou trier le fichier aurait fait
basculer silencieusement tous les clients français vers les DONNÉES anglaises :
pas d'erreur, pas de ligne de journal, juste les mauvais noms partout. La ligne
perdante est supprimée, la survivante documentée, et
dev/rework-docs/_locale_dupes.py signale désormais les clés en double par
fichier de locale. Ce lint rend aussi visible qu'enUS, en tant que locale par
défaut d'AceLocale, fonctionne au contraire en PREMIER-gagnant : la même clé
dupliquée peut se résoudre en entrées différentes selon la langue. 64 conflits
de ce type existent aujourd'hui, presque tous du bruit de traducteurs anciens ;
ils ont été délibérément laissés en l'état et sont au moins visibles maintenant.
Troisièmement, SkuDBEnsureActiveLocaleTables tient enfin sa promesse - il
s'exécute aussi à ADDON_LOADED (auparavant seulement un OnUpdate après
PLAYER_LOGIN, si bien que tout ce qui touchait à [Sku.Loc] dans son propre
gestionnaire PLAYER_LOGIN voyait nil un instant) et ne retourne plus
silencieusement sur un Sku.Loc inutilisable : il valide, journalise le coupable
probable, et construit les tables quoi qu'il arrive.
La parole ne se tait plus quand vous parcourez une liste rapidement
Simple : Faire défiler rapidement un menu, une liste d'objets proches ou vos sacs
pouvait couper la parole entièrement - il ne restait que les sons de clic et de
balise - jusqu'à ce que vous attendiez quelques secondes ou que vous alliez dans
une autre partie de l'interface. Chaque entrée devant laquelle vous passiez était
avalée, et souvent aussi celle sur laquelle vous vous arrêtiez finalement.
Désormais chaque entrée survolée est remise à la voix, et l'entrée sur laquelle
vous vous arrêtez est prononcée. Rien n'a changé quant à l'annonce qui l'emporte :
une nouvelle remplace toujours la précédente, un défilement rapide ne lit donc
toujours que ce sur quoi vous vous arrêtez, et l'interruption de la parole en
combat est inchangée.
Technique : La pompe TTS de Blizzard se cadençait avec un compte à rebours qui
soustrayait un seul delta d'image par exécution, mais son corps ne s'exécute que
toutes les 0,01 s - donc au-dessus d'environ 100 images par seconde elle
soustrayait bien moins que le temps réellement écoulé, et la pause de 0,1 s prévue
avant l'énoncé suivant s'étirait à 0,2-0,3 s réelles. Comme une réinitialisation
de file supprime la ligne qui attend encore, une pause plus longue signifiait plus
d'annonces perdues : le bug empirait à mesure que la fréquence d'images montait.
La pause est maintenant une échéance GetTime, identique à 60 ips et correcte
au-dessus. Mesuré sur une capture à 6-7 appuis de touche par seconde : avant, 7
réinitialisations produisaient 2 lignes prononcées ; après, 7 réinitialisations en
produisent 7. Une seconde protection supprime un StopSpeakingText quand rien n'est
en vol et qu'un autre a déjà été envoyé dans les 0,15 s - le client traite les
arrêts de façon asynchrone, si bien qu'un arrêt tardif pouvait annuler l'énoncé
suivant avant qu'il ne commence. Cette protection ne se déclenche plus jamais
pendant la navigation ; elle demeure pour les rafales événementielles (courrier,
discussion) qui produisaient 17 arrêts en une seconde sans aucune parole.
Moins de travail par ligne prononcée
Simple : Sku effectue nettement moins de travail pour chaque ligne qu'il prononce et
chaque son qu'il joue, ce qui se traduit par un défilement plus fluide.
Technique : Deux diagnostics temporaires [SOUND-PROBE] ont été supprimés. Tous deux
passaient debugstack(...) en argument à dprint, et dprint ne teste l'indicateur de
débogage qu'à l'intérieur de lui-même - un parcours complet de la pile s'exécutait
donc pour chaque son même avec la journalisation désactivée, et celui de
Sku/Core.lua accrochait le PlaySound global, si bien que les propres appels de
Blizzard le payaient aussi. Ils représentaient un quart de toutes les lignes de
journal capturées. La pompe de file saute désormais entièrement ses balayages tant
que les deux files sont vides, au lieu de les exécuter une centaine de fois par
seconde, et la recherche de lien wiki abandonne avant de normaliser la chaîne
plutôt qu'après : elle est appelée pour chaque ligne prononcée depuis
OutputStringBTtts, où son résultat est de toute façon jeté, et sans index wiki elle
effectuait environ 19 passes de string.gsub pour atteindre une garde qui ne faisait
rien.
L'installateur et la page de téléchargement parlent français eux aussi
Simple : Sku lui-même parle français depuis la v42.11, mais tout ce qui l'entoure
non. Le Sku Installer fonctionne désormais en français - il prend sa langue de
Windows, et vous pouvez la changer à tout moment dans la liste « Langue de cet
installateur » - et la page de téléchargement existe en version française,
accessible par les liens de langue en haut de chaque page. Ces notes de version
existent elles aussi en français, à partir de la v42.11. Il n'y a toujours pas
de pack de voix français : sur un client français, Sku parle par votre lecteur
d'écran, et c'est pourquoi l'installateur y propose le pack anglais.
L'installateur passe ainsi en version 4.1.
Technique : Une table française dans le Loc.cs de l'installateur (Lang.Fr, le même
jeu de clés que l'anglais et l'allemand, « fr » repris de la culture
d'interface du système ; la liste des langues est construite à partir de
ComponentsForm._langOrder, plus aucun littéral d'index à maintenir),
docs/index-fr.html, et « Patch Notes Sku FR.txt » que release.ps1 recopie sur
le site. Les réécritures de la page dans release.ps1 parcourent maintenant une
LISTE de pages et s'appuient sur des fragments neutres du point de vue de la
langue, pour qu'une page traduite ne puisse pas conserver un numéro de version
périmé - exactement la panne corrigée pour le titre en v42.11, une page plus
loin ; au passage, le titre de SkuMapper est couvert lui aussi, alors qu'il
dérivait sans que personne le remarque. L'annonce Discord se fait désormais par
canal : une ligne de webhook peut nommer les langues de ce canal, un serveur
uniquement germanophone reçoit donc une seule ligne allemande avec des liens
allemands, au lieu de trois langues qu'il n'a pas demandées.
Deux menus qui n'étaient qu'un détour ont disparu
Simple : Sous « Réglages » se trouvait une entrée « Combat » qui ne contenait
qu'un seul élément : « Menu Sku en combat ». Cet élément se trouve désormais
directement dans « Réglages, Général », et le niveau « Combat » vide au-dessus
n'existe plus. Même chose dans le menu principal : « Outils » ne contenait que
« Dial Targeting » et « Soft Targeting » ; les deux sont maintenant dans le
« Menu rapide », et « Outils » a quitté le menu principal. Les réglages
eux-mêmes ne changent pas, et toutes les valeurs déjà enregistrées sont
conservées.
Technique : Les trois entrées gardent leurs builders, leur db et leur keyPrefix -
seul l'endroit où elles sont accrochées change, les valeurs enregistrées sont
donc intactes. « Werkzeuge » est retiré comme module SkuMenu et de
SkuMenu:SetRootLayout (SkuZOptions/SkuMenu.lua) ; DialTargetingMenuBuilder et
le groupe softTargeting (toujours via IterateOptionsArgs avec
aIncludeHidden=true, puisqu'il est marqué forAudioMenu=false) sont désormais
accrochés dans le BuildChildren du menu rapide (SkuZOptions/Core.lua). La
spécification « Kampf » sort de tSpecs dans SkuCore/Options.lua ;
CombatMenuOpenMenuBuilder est maintenant appelé à la fin du builder Général,
sur le même réglage combatMenuOpen. Aucun chemin SlashFunc ne pointait vers
l'un de ces deux menus, il n'y avait donc rien d'autre à suivre.
-------------------------------------------------------------------------------------------------------
Changements dans Sku v42.11
Maître de vol : Sku nomme le prochain point de vol et peut vous y faire descendre
Simple : Sur un trajet en taxi chez le maître de vol, Sku annonce désormais quel point
de vol arrive ensuite, et vous pouvez y descendre au lieu de voler jusqu'à la
destination. La touche pour cela est CTRL-MAJ-E par défaut ; comme toutes les
autres touches de Sku, elle est réassignable, listée sous « Navigation et points
de passage ». Il y a deux annonces par étape parce qu'elles répondent à des
questions différentes : le prochain arrêt au décollage et après chaque saut, et
« atterrissage anticipé possible » environ 800 mètres avant. Avant le dernier
arrêt, Sku reste silencieux - y atterrir en avance revient simplement à arriver.
L'ensemble peut être désactivé sous « Maître de vol ». Uniquement les taxis des
maîtres de vol, jamais les véhicules.
Technique : Nouveau module SkuCore/taxi.lua. La route est enregistrée, pas devinée :
hooksecurefunc sur TakeTaxiNode se déclenche pendant que la carte des taxis est
encore ouverte, la seule fenêtre où GetNumRoutes, TaxiGetNodeSlot et TaxiNodeName
répondent encore. La liste des arrêts est donc exacte, et chaque arrêt qui y figure
est réellement accessible pour ce personnage. Un curseur de proximité la fait
avancer à mesure que le taxi survole chaque nœud ; la recherche du nœud le plus
proche ne subsiste que comme repli signalé pour un /reload en plein vol, filtré par
faction et par découverte. Trois particularités du client sont consignées dans
l'en-tête du fichier pour ne pas avoir à être redécouvertes : la demande vous fait
toujours atterrir au point de vol SUIVANT (comme le dit aussi
TAXI_CANCEL_DESCRIPTION), ce qui explique pourquoi le curseur de route est la bonne
réponse ; IsPossessBarVisible renvoie false pendant que PossessButton2 est affiché -
le seul élément fiable est MainMenuBarVehicleLeaveButton, lu avec IsVisible, et il
est présent pendant tout le vol ; et PLAYER_CONTROL_LOST se déclenche alors
qu'UnitOnTaxi est encore false, de sorte que tout nettoyage branché dessus
effacerait des données fraîchement enregistrées. Chaque point d'entrée revérifie
UnitOnTaxi - le même test que Blizzard utilise pour choisir TaxiRequestEarlyLanding
plutôt que VehicleExit. Diagnostics : lignes « taxi: » dans SkuDebugLog et /skutaxi.
Les invitations de groupe ne sont plus refusées silencieusement
Simple : Fermer le menu de Sku - pour quelque raison que ce soit : sortir des sacs avec
Échap, ouvrir une autre fenêtre, un passage en combat - refusait une invitation de
groupe en attente, et la personne qui vous invitait voyait un refus. Pour un joueur
voyant, il ne se passe absolument rien à ce moment-là ; la boîte de dialogue reste
simplement là pendant ses 60 secondes. C'était donc une perte que seuls les
utilisateurs de Sku subissaient. L'invitation n'est plus répondue à la fermeture du
menu ; elle quitte simplement l'écran et reste accessible sous « Local ».
Technique : StaticPopupDialogs["PARTY_INVITE"].OnHide appelle DeclineGroup à moins que
dialog.inviteAccepted ne soit défini - et OnHide se déclenche bel et bien sur un
simple frame:Hide() (OnHide XML -> GameDialogMixin:OnHide -> StaticPopup_OnHide ->
dialogInfo.OnHide). Le OnHide du menu de Sku exécute exactement ce
StaticPopup1:Hide(). Nouveau : SkuCore:NeutralizePopupHide(frame), appelé juste
avant : pour PARTY_INVITE il pose inviteAccepted afin que le OnHide de Blizzard
saute le refus, et marque le cadre pour que notre propre hook de masquage
distingue un masquage neutralisé d'une vraie réponse - de simples champs sur un
cadre non sécurisé, aucune contamination ; OnShow réinitialise inviteAccepted, de
sorte qu'un affichage ultérieur véritable n'est pas affecté. Il n'existe pas d'API
« invitation en attente » sur ce client, le cache est donc le nôtre : alimenté par
PARTY_INVITE_REQUEST, abandonné sur PARTY_INVITE_CANCEL, sur tout masquage non
neutralisé de la boîte de dialogue (une vraie acceptation, un refus ou une
expiration) et par sa propre échéance. Pour mémoire, toutes les boîtes de dialogue
dont le OnHide est destructeur ont été auditées : PARTY_INVITE (corrigé ici),
LEVEL_GRANT_PROPOSED (DeclineLevelGrant), EQUIP_BIND et apparentées
(CancelPendingEquip - pas d'indicateur d'esquive, demande un autre correctif) et
QUIT (CancelLogout, sans danger).
Les demandes en attente restent accessibles dans le menu Local
Simple : Quand vous quittez le menu de Sku avec Échap, une boîte de dialogue ouverte
disparaît de l'écran - une invocation, la demande de libérer l'esprit, une offre de
résurrection, une vérification de préparation. Jusqu'ici elle était perdue jusqu'au
prochain /reload, alors même qu'elle restait valide sur le serveur pendant tout ce
temps. De telles demandes apparaissent désormais comme une entrée à part
directement sous « Local », nommée d'après la demande ; DROITE ou ENTRÉE affiche à
nouveau la vraie fenêtre, et Sku y descend pour que vous utilisiez les boutons
habituels.
Technique : Nouveau module SkuCore/pendingPrompts.lua. Les demandes sont réémises à
partir de l'ÉTAT, pas du cadre : l'invocation via C_SummonInfo, la résurrection via
ResurrectGetOfferer, la libération après la mort via GetReleaseTimeRemaining plus
les options de résurrection personnelle de C_DeathInfo, la vérification de
préparation via GetReadyCheckTimeLeft/Status. Un :Hide() brut n'atteint que
StaticPopup_OnHide et jamais OnCancel, rien n'était donc jamais refusé ni libéré -
PLAYER_ENTERING_WORLD est le seul endroit où Blizzard réaffiche DEATH et
CONFIRM_SUMMON, d'où le « perdu jusqu'au /reload ». Tant que sa propre boîte de
dialogue est à l'écran, l'entrée est supprimée, elle ne fait donc jamais doublon
avec le nœud issu du balayage de la popup. Il est important de noter que ces
entrées sont PASSIVES : le prédicat « quelque chose est-il ouvert » dans
CheckFrames a un double rôle - il maintient un menu ouvert en vie et il force
l'ouverture du menu via SlashFunc. Les demandes en attente n'alimentent que le
premier (tPendingOnly), sans quoi une demande accessible en permanence arracherait
le menu à chaque mise à jour des sacs ; le même raisonnement les tient à l'écart de
combatMenuHasWindow. La première forme était imbriquée, avec ses propres feuilles
d'action - fonctionnel, mais trop de navigation. C'est maintenant un unique nœud
plat par demande : kind="action" avec dynamic=true, uniquement pour que le
gestionnaire de touches achemine DROITE vers OnSelect, plus une surcharge d'OnSelect
qui contourne la mécanique de descente générique ; les vrais boutons arrivent par le
balayage normal de la fenêtre réaffichée.
Échange : ce qui se trouve dans la fenêtre d'échange est désormais annoncé en direct
Simple : Faire un clic droit sur un objet pour le mettre dans la fenêtre d'échange était
complètement silencieux, et son entrée dans le menu des sacs restait inchangée. Sku
dit maintenant « dans l'emplacement d'échange N » ou « emplacement d'échange
N vidé », y compris pour le côté de votre partenaire d'échange, ce qui n'était
auparavant ni visible ni audible. Les listes « Vos objets » et « Objets du
partenaire » se rafraîchissent d'elles-mêmes ; l'entrée « Actualiser » n'est plus
nécessaire pour cela. Tant qu'un objet est dans l'échange, son entrée de sac porte
un préfixe « en échange », prononcé avant le nom de l'objet, et à l'instant où le
marqueur apparaît sur l'entrée où vous vous trouvez, Sku dit seulement « en
échange » - il ne vous relit pas tout le nom de l'objet. Et après un échange
terminé, l'objet que vous avez donné disparaît désormais de façon fiable de la liste
des sacs, au lieu de seulement parfois.
Technique : La mécanique existante de vente et de banque ne pouvait pas s'appliquer ici :
mettre un objet dans la fenêtre d'échange ne le retire pas du sac. Le client se
contente de poser isLocked sur l'emplacement (le propre ContainerFrame de Blizzard
réagit à ITEM_LOCK_CHANGED en désaturant simplement l'icône), aucun BAG_UPDATE n'est
donc déclenché et il n'y avait rien à quoi les gestionnaires conditionnés puissent
réagir. Les objets ne quittent les sacs que lorsque l'échange S'ACHÈVE - et ce
BAG_UPDATE fait la course avec le CheckFrames de TRADE_CLOSED, alors que
Sku.tBagPostAction avait déjà expiré 2,5 s après le clic droit. Perdre cette course
était le symptôme « parfois il reste dans la liste ». Nouveau :
Sku.tBagSettleWindow, une seconde barrière plus étroite dans
SkuCore:BAG_UPDATE_DELAYED, armée pour 3 s par TRADE_CLOSED, qui pilote le
SkuBagIdleRefresh SILENCIEUX - pas la confirmation parlée, puisque aucun curseur
n'est ancré sur l'objet échangé - plus un repli d'1 s pour la course inverse.
TRADE_PLAYER_ITEM_CHANGED et TRADE_TARGET_ITEM_CHANGED sont enregistrés pour la
première fois, dédoublonnés par emplacement selon l'objet et la quantité et
conditionnés à TradeFrame:IsVisible pour que les salves de fin d'échange restent
silencieuses. Les deux partagent SkuCore:TradeMenuRefresh, une reconstruction
silencieuse temporisée. Le marqueur « en échange » est un préfixe : dans une liste
de sac il suit le numéro de position, dans la liste « tous les objets » il est
ajouté après le tri (comme le préfixe « nouveau »), de sorte que le marqueur
transitoire ne déplace jamais un objet hors de sa place alphabétique. L'annonce est
accrochée au réancrage silencieux de SkuBagIdleRefresh, qui compare le marqueur sur
l'entrée ciblée avant et après la reconstruction et ne parle que s'il est apparu -
auto-dédoublonnant, puisque les rafraîchissements suivants le voient des deux côtés.
SkuCore:TradePartnerName est désormais la source unique du nom du partenaire, y
compris pour l'annonce existante de l'argent échangé.
Combat : Sku annonce quand un sort a été interrompu
Simple : Sous Hostile -> Incantation, « Annoncer les interruptions » existe désormais
séparément pour votre cible actuelle et pour tous les ennemis - exactement comme les
annonces d'incantation elles-mêmes. Cela permet d'entendre les interruptions sur
votre propre cible sans entendre toutes celles du raid. Quand vous ou votre familier
interrompez, vous entendez « Vous avez interrompu le sort » avec le nom du sort ;
quand quelqu'un de votre groupe interrompt, « sort interrompu ». Seule l'annonce
pour votre cible actuelle est activée par défaut, tous les ennemis reste à activer
vous-même ; celles et ceux qui l'avaient désactivée restent tranquilles.
Technique : SPELL_INTERRUPT depuis le journal de combat via le nouvel événement du
répartiteur SKU_SPELL_INTERRUPT. L'ancien interrupteur unique outputInterrupts est
remplacé par yourTargetInterrupts et allEnemiesInterrupts ; seul
yourTargetInterrupts est prérempli depuis l'ancienne valeur là où c'était nil,
allEnemiesInterrupts démarre désactivé. Avec l'interrupteur global désactivé,
une interruption n'est annoncée que si le destGUID de l'incantation interrompue est
votre cible actuelle ; avec les deux activés, elle n'est toujours annoncée qu'une
fois. « Vous » signifie une affiliation de source MINE, ce qui couvre votre propre
familier (le Blocage de sort du chasseur corrompu, par exemple).
Combat : les annonces d'incantation peuvent être limitées aux sorts interruptibles
Simple : Nouveau réglage « N'annoncer que les sorts interruptibles » (désactivé par
défaut) sous Hostile -> Incantation. Une fois activé, Sku ne signale plus les
incantations qu'une interruption n'affecterait de toute façon pas.
Technique : La valeur de retour notInterruptible d'UnitCastingInfo et d'UnitChannelInfo
est une donnée morte sur le client Anniversary 2.5.6 - elle revenait nil à chaque
incantation tandis que la 9e valeur de retour livrait le bon spellId ; la propre
barre d'incantation de Blizzard force l'indicateur à false pour BC et antérieur.
L'interruptibilité provient donc d'une liste statique, nouvelle sous
SkuDB/uninterruptibleCasts.lua, importée de ClassicCastbars (licence MIT). Elle
couvre l'ancien monde ; des tables de fusion SkuTbcAdditions vides sont prêtes pour
nos propres trouvailles TBC. La correspondance se fait par spellID, par nom
localisé, ou par npcID plus nom.
Ciblage souple : « si aucune cible n'est verrouillée » atteint enfin réellement le client
Simple : Le réglage qui détermine si le ciblage souple est utilisé pour interagir n'a
jamais rien fait. La touche d'interaction ne se souciait pas de savoir si vous aviez
une cible verrouillée - un cadavre ou une chaise à côté captait l'appui de touche
quand même. C'est corrigé, et corrigé dans le client plutôt que dans une émulation.
À noter : ces réglages du client sont verrouillés pendant le combat. Sku les
maintient donc au repos dans l'état où la suppression est déjà en place, au cas où
quelque chose vous saute dessus et que vous ne le cibliez qu'une fois le combat
engagé. Deux cas limites subsistent : si vous tuez votre cible, l'état persiste
jusqu'à votre prochain changement de cible - pendant ce laps de temps les objets
proches ne sont pas adressables (le cadavre que vous avez ciblé se pille
normalement). Et si vous entrez en combat en tenant une cible non attaquable, le
ciblage souple reste actif pour ce combat. Cela demanderait une écriture pendant le
combat et ne peut pas être résolu depuis un addon.
Technique : Les 32 CVars SoftTarget sont toujours enregistrées dans la 2.5.6 (repérées
dans WowClassic.exe) - le client n'est donc pas en cause. Les deux branches de
l'ancien if/else écrivaient SoftTargetMatchLocked = 0, du code mort identique dans la
v32.31 et dans la v41.04 ; le réglage n'a jamais quitté Lua. La 41.05 a ensuite écrit
1, mais a fusionné l'option 2 dans l'option 1, et SoftTargetWithLocked - la CVar qui
régit réellement ceci - n'a jamais été écrite par aucune version de Sku. Mesuré sur
la 2.5.6.68575, hors combat, un cadavre et une créature vivante à portée, touche
d'interaction pressée avec la créature verrouillée comme cible : WithLocked 0 agit
sur la créature et ignore le cadavre, 1 et 2 pillent tous deux le cadavre. Seul 0
supprime donc le ciblage souple tant qu'une cible est verrouillée, et il n'existe pas
de valeur intermédiaire « seulement si attaquable ». L'option correspond désormais
directement à cela : 0 (toujours) -> WithLocked 2, MatchLocked 0 ; 1 (si aucune cible
verrouillée) -> WithLocked 0, MatchLocked 1 ; 2 (aucune cible attaquable) ->
MatchLocked 2, avec WithLocked basculé entre 0 et 2 sur PLAYER_TARGET_CHANGED. L'état
de repos est 0, parce que WithLocked n'agit que tant qu'une cible est verrouillée -
s'y reposer ne coûte rien et couvre le seul cas que les CVars ne pourraient autrement
jamais atteindre. 2 n'est désormais écrit que pour le seul état qui a véritablement
besoin du ciblage souple AVEC une cible verrouillée : une cible que vous ne pouvez
pas attaquer - un cadavre que vous pillez, un PNJ à qui vous parlez, un joueur que
vous suivez. Supprimés : le ticker de SkuMob qui réécrivait SoftTargetInteract quatre
fois par seconde, son doublon dans PLAYER_TARGET_CHANGED et l'état fantôme
interactTempDisabled - cette émulation ne pouvait pas fonctionner du tout, puisque
les CVars sont protégées en combat. tSetSoftTargetCVar vérifie maintenant chaque
écriture en relisant la CVar (comparaison numérique, pour qu'un « 15.000000 »
normalisé ne soit pas un faux échec) et réarme la relecture sur
PLAYER_REGEN_ENABLED lorsqu'une écriture est refusée, au lieu de mettre en cache une
valeur qui n'a jamais abouti. Une écriture en combat journalise « softTarget refused
... {want=3, got=0} » et lève ADDON_ACTION_BLOCKED.
Menu : ENTRÉE continuait d'agir dans le menu invisible après sa fermeture
Simple : Après avoir sélectionné un point de passage - et dans des cas semblables - le
menu se fermait correctement, mais chaque nouvel appui sur ENTRÉE cliquait encore
dans le menu invisible : le son de navigation se jouait, la sélection se relançait
(« nouvelle navigation »), et ENTRÉE n'ouvrait plus la fenêtre de discussion. En
plus de cela, le menu pouvait rester bloqué ouvert si une touche de déplacement était
enfoncée au même instant : le point de passage était posé, mais le menu restait
ouvert et gardait les touches capturées. Les deux sont corrigés.
Technique : Deux causes distinctes. Premièrement, OnPostSelect continue après l'action
qui a fermé le menu : il repointait currentMenuPosition sur le selectTarget -
écrasant la remise à la racine faite par la fermeture - et déclenchait son OnEnter ;
le OnEnter générique réarmait alors sans condition les liaisons de substitution
SKU_KEY_MENULEFTCLICK sur le bouton sécurisé. Les liaisons de substitution sur un
cadre MASQUÉ sont pleinement actives, ENTRÉE était donc ressuscitée juste après que
OnHide l'avait effacée. Désormais le OnEnter générique n'arme les touches de clic
que tant que le bouton sécurisé est affiché, et OnPostSelect s'arrête avant le
refocus final quand le menu n'est plus ouvert (le menu de combat sans affichage est
exempté via combatMenuActive). Deuxièmement, CloseMenu passe par le même
gestionnaire de bascule que l'ouverture, dont les deux barrières de report en cas de
déplacement annulaient silencieusement la fermeture - l'une lit les indicateurs de
déplacement en direct, l'autre le SkuCore.isMoving mis en cache, ce qui explique
pourquoi un chemin passait et l'autre avortait. Les demandes de fermeture contournent
maintenant ces barrières ; Hide et ClearOverrideBindings sont sans danger pendant le
déplacement, et une fermeture réussie atteint de nouveau les remises à zéro
d'openMenuAfter. Par précaution supplémentaire, le ticker de réouverture désarme les
indicateurs qui ne peuvent de toute façon jamais se déclencher et ne relance la mise
en place de la navigation que lorsque le menu est visiblement ouvert mais insensible
aux touches (nouveau : SkuOptions.menuNavKeysBound).
Avertissement de collision pendant la marche automatique vers la cible
Simple : L'avertissement indiquant que vous marchez dans quelque chose ne s'armait que
tant que vous mainteniez vous-même une touche de déplacement. Avec la fonction
« Interagir avec la cible » du jeu, le moteur vous y conduit sans qu'aucune touche ne
soit maintenue - et là, vous pouviez vous coincer sur un obstacle sans vous en rendre
compte. Ces déplacements sont désormais couverts eux aussi.
Technique : Un nouvel indicateur EngineMoving, issu de PLAYER_STARTED_MOVING et
PLAYER_STOPPED_MOVING, arme l'avertissement basé sur les coordonnées pour les
déplacements pilotés par le moteur également. PLAYER_STOPPED_MOVING ne se déclenche
pas tant que vous poussez contre un mur - précisément le cas pour lequel
l'avertissement existe (démontré par la purge de l'AutoRun périmé). La rotation pure,
la chute et le suivi automatique sont exclus ; le suivi a son propre avertissement de
collision.
Moins d'erreurs en instance et en raid
Simple : Deux sources d'erreurs qui produisaient des erreurs Lua constantes à l'intérieur
des instances sont corrigées - perceptible sous forme de messages d'erreur qui
n'apparaissent plus et de combats de boss plus calmes.
Technique : Premièrement, UnitPosition("player") renvoie nil à l'intérieur des instances,
des raids et des champs de bataille, si bien que SkuNav:Distance renvoyait nil et que
GetUnsortedAvailableQuestsTable plantait sur une concaténation à chaque
QUEST_LOG_UPDATE (24 fois en une seule session de raid). La convention existante des
99999 mètres s'applique désormais : la quête reste listée et se trie en dernier.
Deuxièmement, ClearOverrideBindings et SetOverrideBindingClick sont protégées en
combat, et le pcall qui les entoure ne supprime pas l'événement ADDON_ACTION_BLOCKED
(84 fois en un seul combat, plus des avertissements de limite d'exécution de
BugGrabber). L'application de la liaison de touche d'AtlasLoot est maintenant sautée
pendant le combat et retentée une fois via PLAYER_REGEN_ENABLED.
« Interrompre temporairement le suivi pendant une incantation » est réactivé pour un test
Simple : Ce réglage figurait dans le menu, mais sa fonction était désactivée depuis
l'état amont de la v41.06. Il est réactivé, désactivé par défaut, afin d'établir si
ce client permet ne serait-ce que de reprendre le suivi automatiquement. Si vous
l'activez, attendez-vous à ce que le suivi ne revienne pas de lui-même après une
incantation.
Technique : Les corps d'UnfollowOnCast et de FollowOnCast étaient mis en commentaire,
pour une raison qui n'a jamais été consignée. Ils répondent maintenant à une
question : ce client exécute-t-il FollowUnit depuis un contexte d'événement serveur,
c'est-à-dire sans événement matériel, ou est-ce silencieusement bloqué ? Le canal de
preuve est la sortie vocale AUTOFOLLOW_END/BEGIN et les lignes « followcast » dans
SkuDebugLog, y compris une vérification d'état 0,5 s après l'appel. Corrigé en
chemin : dans les gestionnaires CHANNEL_START/STOP, unitTarget et aUnitTarget ne
concordaient pas, si bien que l'arrêt et la reprise du suivi ne se déclenchaient
jamais pour les sorts à canalisation.
Courrier : après l'envoi d'une lettre, Sku relisait plus tard le texte saisi
Simple : Un certain temps après l'envoi d'une lettre, Sku se mettait à répéter les
caractères qui avaient été tapés - parfois longtemps après la fermeture de la boîte
aux lettres. Deux bugs indépendants en étaient la cause. Premièrement, une fois que
vous aviez ouvert une boîte aux lettres, elle comptait comme ouverte en interne
jusqu'au prochain /reload, si bien que chaque courrier arrivant plus tard
reconstruisait le menu et réannonçait l'entrée courante - où que vous soyez dans le
menu. Si vous vous trouviez sur un champ de lettre, cette entrée était le texte que
vous veniez de taper. Deuxièmement, la lecture caractère par caractère pendant la
saisie ajoutait chaque caractère à la fin de la file de parole : si vous tapez plus
vite que la voix ne parle, cela accumule un retard, et fermer le champ de saisie n'y
mettait pas fin - la lecture continuait simplement.
Désormais la lecture suit votre frappe : si elle prend trop de retard, ce qui n'a pas
encore été prononcé est abandonné au lieu de traîner. « Envoyé » et la confirmation
d'un champ arrivent immédiatement, plus derrière tout ce que vous avez tapé. Et une
mise à jour de la boîte de réception n'a d'effet que tant que la boîte aux lettres
est réellement ouverte et que vous êtes dans la branche Courrier du menu. Deux
petites choses au même endroit : le champ de collage exécutait son action OK n fois à
la n-ième ouverture, et les deux champs de saisie de Sku libèrent maintenant le
clavier de façon fiable quand ils se ferment.
Technique : MailboxOpenFlag était posé dans MAIL_SHOW mais n'était effacé nulle part ;
MAIL_CLOSED l'efface désormais. MAIL_INBOX_UPDATE n'arrive pas seulement quand une
boîte aux lettres est ouverte - le serveur l'envoie aussi lorsque du courrier arrive
plus tard - et il filait sans contrôle dans le OnUpdate générique
(SkuZOptions/templates.lua). Ce n'est pas un rafraîchissement discret : il jette les
enfants du niveau courant, les reconstruit et appelle VocalizeCurrentMenuName. En
plus de l'indicateur, nous vérifions maintenant MailFrame:IsVisible() et remontons la
chaîne des parents jusqu'au nœud contributeur Local L["Mail"] - ce n'est que là
qu'une reconstruction est un rafraîchissement et non une intrusion. L'appel forcé à
SlashFunc sans position de menu ne subsiste que pour le cas authentique de
l'ouverture de la boîte aux lettres ; un événement d'arrière-plan ne doit jamais
forcer l'ouverture du menu.
La lecture de frappe de la v42.08 ajoute au lieu d'écraser à dessein, sans quoi une
frappe rapide avale des caractères - mais elle n'avait aucun plafond. À environ 0,4 s
par caractère contre quatre à cinq frappes par seconde, la file croît sans limite, et
le défilement pousse ce retard dans C_VoiceChat.SpeakText à environ 100 entrées par
seconde, là où Sku ne peut plus le rattraper. D'où deux nouvelles méthodes dans
SkuVoice-1.0, GetBttsQueueDepth et TrimBttsQueue(aKeep) : seul ce qui ATTEND encore
dans la file propre à Sku est abandonné, jamais ce qui a déjà été transmis - c'est
pourquoi cela ne peut pas couper un mot en deux, et pourquoi c'est légitime même avec
« ne jamais réinitialiser les files », un réglage qui concerne l'interruption d'une
parole en cours. Le plafond est de trois caractères en attente. Le retard est
abandonné avant l'exécution du rappel OK, et MAIL_SEND_SUCCESS ainsi que la
confirmation de champ dans MailEditor parlent maintenant en écrasant plutôt qu'en
ajoutant. EditBoxPasteShow attachait son OnEnterPressed via HookScript à CHAQUE appel
- HookScript ne remplace pas, il enchaîne - si bien que le rappel OK s'exécutait n
fois à la n-ième ouverture ; il vit désormais dans la branche exécutée une seule fois
à la création. Les deux champs de saisie appellent ClearFocus lorsqu'ils se masquent,
parce que le champ est un petit-enfant du cadre masqué et que le focus clavier
resterait sinon sur une zone d'édition invisible.
Niveau d'objet : tous les objets étaient annoncés avec le même chiffre
Simple : Le niveau d'objet dans les descriptions d'objets était faux depuis qu'il
existe. Il n'appartenait pas du tout à l'objet que vous étiez en train de lire - il
appartenait au dernier objet consulté quelque part dans Sku, généralement des heures
plus tôt, et il le restait pour toute la session. C'est pourquoi il lisait le même
chiffre sur chaque objet, et pourquoi c'était si souvent un 1 : une clé ou un objet
de quête, le genre de chose qui est consultée tôt et qui est réellement de niveau
d'objet 1. Un /reload n'aidait pas, car le mauvais chiffre n'était pas une donnée
périmée que l'on rafraîchit - c'était simplement le mauvais objet qui était
interrogé. Ce même mauvais objet décidait aussi de la qualité ajoutée aux noms
d'objets. Les deux sont corrects maintenant. Le niveau d'objet n'est en outre
annoncé que pour l'équipement que vous pouvez réellement porter : les clés, les
objets de quête, les composants et les consommables sont tous de niveau d'objet 1
par définition, cette ligne ne disait donc rien à leur sujet et elle a disparu. Elle
a également disparu des endroits où elle n'avait jamais sa place, comme les
descriptions de buffs et les résistances de la feuille de personnage.
Technique : Le GetItem() d'une infobulle est un cache rémanent, pas une description de ce
que le cadre affiche : ClearLines() ne le réinitialise pas, et un SetX() qui échoue à
remplir non plus - un objet non mis en cache, le conteneur de banque -1, ou toute
infobulle de sort, d'aura ou de caractéristique qui n'a aucun objet.
SkuScanningTooltip est créée une seule fois à PLAYER_LOGIN et n'est jamais
réattribuée, si bien que son GetItem() continue de répondre avec le dernier objet qui
s'est jamais résolu à travers elle. En plus de cela, TooltipLines_helper lisait le
lien depuis SkuScanningTooltip quelles que soient les régions qu'on lui avait
passées, et la plupart des appelants lui passent celles de GameTooltip. Deux défauts
plus petits aggravaient le tout : la deuxième valeur de retour de GetSpell(), qui
n'est pas un lien, était affectée à ItemLink et transmise à GetDetailedItemLevelInfo ;
et un objet non mis en cache produit un lien VIDE, qui est vrai en Lua et passait tout
droit à travers la garde « if ItemLink then ». La résolution vit désormais dans
SkuUtil - TooltipFirstLine, TooltipItemLink, ItemQualityString, ItemLevel - et ne fait
confiance à GetItem() que lorsque le nom qu'il rapporte correspond à la première ligne
affichée de l'infobulle, qui est le seul point de vérité disponible. Les trois blocs
dupliqués dans LocalMenu et utilities s'y ramènent ; tScanTooltipRegions résout
lui-même le lien, la qualité et le niveau d'objet et renvoie le lien validé, de sorte
que les appelants en aval obtiennent celui que le texte décrit. La condition
« équipable » lit equipLoc depuis GetItemInfo et se rabat sur
C_Item.GetItemInventoryTypeByID tant que le cache d'objets est encore froid.
Français : Sku est désormais disponible en français
Simple : Sku peut désormais être utilisé en français - testé sur un client français.
Environ 95 pour cent des textes de menu et d'annonce sont traduits (3063 entrées sur
3220), et tout le reste retombe automatiquement sur l'anglais, il n'y a donc jamais
de trou ni d'entrée vide. Deux choses qui manquaient encore ont été ajoutées : les
noms de routes et de points de passage de la chaîne de traduction ont leur propre
table française de 153 entrées, dont les noms de lieux sont importés d'une capture
faite sur un vrai client français plutôt que devinés, et les 158 endroits où du
texte est conservé de façon bilingue dans le code portent maintenant aussi une
version française. Les noms du JEU - créatures, quêtes, objets, éléments du décor -
sont importés en bloc depuis Questie, ce sont donc les propres chaînes françaises de
Blizzard et non des traductions automatiques, et elles correspondent à ce qu'affiche
un client français, ce qui est précisément ce qui garde les noms de routes et de
points de passage cherchables. Les noms de zones viennent directement du client. La
sortie vocale n'existe toujours qu'en allemand et en anglais ; toutes les autres
langues de client utilisent les fichiers audio anglais.
Pour les joueurs allemands et anglais, rien ne change dans le texte - mais
l'utilisation de la mémoire, si : Sku ne construit plus que les tables de noms de sa
propre langue, plus l'anglais comme repli, au lieu de toutes les construire
systématiquement.
Technique : Sku.Locs est maintenant une liste ORDONNÉE de trois locales, et les noms de
routes empaquetés dans SkuNav:LoadDefaultMapData sont découpés par position d'après
elle au lieu de l'être par un string.match avec un « (.+) » gourmand de part et
d'autre du séparateur. Cette ancienne analyse n'était pas seulement limitée à deux
locales : « .+ » est gourmand, donc avec trois champs « a », « b » et « c » les deux
premiers seraient arrivés comme un unique champ anglais et seul le dernier comme le
champ allemand. Les fichiers ayant moins de champs que de locales restent valides et
retombent sur enUS. Sku.LocAudio sépare la locale AUDIO de la locale de données, et
deux bugs préexistants sont apparus et ont été corrigés en chemin :
SkuAudioFileIndexIntegrated[Sku.Loc] n'était pas protégé et indexait nil sur toute
langue de client sans sous-table propre, et GetAudiodata n'avait aucun repli sur la
casse alors que l'index enUS stocke ses identifiants en minuscules et que les
appelants les passent avec une majuscule - ce qui explique pourquoi l'annonce
d'entrée et de sortie de zone est silencieusement morte sur les clients anglais.
ChunkLoader a désormais une barrière de locale : auparavant il construisait TOUS les
blocs enregistrés quelle que soit la langue du client, si bien qu'un client allemand
matérialisait aussi les tables de noms anglaises complètes et inversement. La règle
est maintenant « locale active plus enUS », avec objectLookup.deDE structurellement
exempté parce que l'objectLookup anglais est DÉRIVÉ de son ensemble de clés - le
filtrer naïvement aurait laissé les clients anglais sans aucun nom d'élément du
décor. Les quatre fusions .deDE codées en dur sont devenues SkuDBMergeLocales, ce qui
corrige au passage un bug latent : un client français aurait obtenu les noms TBC de
base mais silencieusement aucun nom WotLK. Après la fusion, les trous de la locale
active sont comblés depuis enUS (Questie n'a pas de nom français pour environ 2700
identifiants que Sku connaît) ; délibérément une étape de construction plutôt qu'une
métatable __index, parce que pairs() ne traverse pas __index et que plusieurs
consommateurs ÉNUMÈRENT ces tables au lieu de les indexer - une métatable aurait
corrigé les recherches et laissé discrètement les menus incomplets. deDE et enUS sont
exclus du comblement pour que la construction allemande reste identique octet pour
octet (vérifié, 37 jeux de données sur 37). Le fichier de locale lui-même est
Sku/locales/frFR.lua, généré à partir de fichiers partiels : les entrées non
traduites conservent leur valeur anglaise, la table est donc toujours complète et du
Lua valide, et AceLocale renvoie de toute façon nil pour de/en - là, le fichier ne
coûte qu'une analyse et rien d'autre. Les espaces de début et de fin sont porteurs de
sens dans cette table mais ne survivent pas à un aller-retour TSV, ils sont donc
réappliqués depuis chaque original anglais pendant l'assemblage, et le validateur
contrôle la chaîne FINALE pour le nombre de marqueurs, les espaces en bordure et le
nombre de points-virgules. Pour tester sans un second client, il y a /skudebug locale
, qui force la locale de DONNÉES et persiste ; elle s'applique à
ADDON_LOADED plutôt qu'à PLAYER_LOGIN, parce que la construction différée des routes
(mesurée à 1,87 s contre 2,01 s pour PLAYER_LOGIN) décide déjà quels champs de noms
sont matérialisés. Trois erreurs bloquantes sur un client forcé en français sont
corrigées : maps.lua est une table simple, non découpée en blocs, et ne portait
AreaName_lang/Name_lang que pour deDE et enUS - la locale manquante est maintenant
remplie une fois à ADDON_LOADED depuis C_Map (GetAreaInfo et GetMapInfo ; 2819
entrées, dont 1672 issues du client), ce qui donne de vrais noms Blizzard sans avoir
à livrer de données de noms de zones ; SkuDB.SpellDataTBC imbrique ses locales À
L'INTÉRIEUR de chaque entrée, si bien que rien ne les a jamais localisées et que onze
endroits levaient une erreur en frFR - la locale active pointe maintenant sur la
sous-table anglaise par référence (pas de copie, pas de chaînes supplémentaires), ce
qui suffit pour des noms utilisés surtout comme clés de correspondance d'auras ; et
SkuDB.objectResourceNames est indexé par nom d'élément du décor et par locale et
était lu sans protection pendant la construction du cache de points de passage - un
repli anglais y aurait été faux (il n'aurait jamais correspondu et aurait
silencieusement transformé chaque minerai et chaque plante en point de passage
ordinaire), l'ensemble est donc dérivé à travers les identifiants d'objets à la
place.
Toute la branche française a depuis été jouée de bout en bout sur un client français
et fonctionne : menus, noms et routes s'affichent en français, et là où aucune
traduction n'existe, le repli anglais prend le relais sans trou. La table de routes
routeOverrides_frFR.lua et le troisième argument sur chaque site Sku.deEn en font
partie. Comme zhCN et ruRU tournent avec Sku.Loc = enUS, la même passe les couvre
aussi.
Page de téléchargement : le titre annonçait une version périmée
Simple : Sur la page de téléchargement, le titre de l'addon principal indiquait encore
« Version 42.06 » alors que le lien en dessous pointait correctement sur la 42.10.
Comme les utilisateurs de lecteur d'écran naviguent par titres, la première chose
annoncée dans cette section était la mauvaise version - on aurait cru que la page
servait un Sku obsolète. Le titre et le lien concordent désormais.
Technique : release.ps1 réécrivait le href et le texte du lien à chaque publication mais
jamais le titre, qui était donc figé depuis la 42.06 (17/07/2026) sur quatre
publications. Le titre fait maintenant partie de Set-DocsSkuLink et ne peut plus
dériver. Le zip lié était correct depuis le début (vérifié : Sku-42.10.zip contient
« ## Version: 42.10 »).
Balises : un son distinct pour le dernier point de passage d'une route
Simple : Il existe désormais un troisième son de balise à côté de ceux des petites et
des grandes balises : le son de la dernière balise d'une route. Il ne se joue que
pour le dernier point de passage d'une méta-route, vous pouvez donc entendre si le
point vers lequel vous marchez est la destination ou seulement une étape de plus. Le
réglage se trouve sous Moniteur -> Balise, juste en dessous des sons de petite et de
grande balise, et sa valeur par défaut est Balise 3. Suivre un point de passage isolé
est inchangé - cela utilise toujours le son petit ou grand selon la taille du point
de passage.
Technique : Nouveau réglage de profil SkuNav beaconSoundSetLast (« Beacon 3 » par
défaut), déclaré dans le schéma SkuNav et ajouté à la liste blanche explicite des
nœuds du sous-menu Balise dans SkuCore/aq.lua - cette liste n'est pas la table args
complète, un nouveau nœud d'option doit donc y être nommé aussi, sans quoi il
n'apparaît jamais dans le menu. GetBeaconSoundSetName prend un deuxième argument et
renvoie le jeu de la dernière balise lorsqu'il est vrai, laissant intact le
comportement étroit/large et les appelants à un seul argument (la balise de panique,
le hook TomTom). Le nouveau SkuNav:IsLastMetaRouteWp compare le NOM du point de
passage à la dernière entrée de metapathFollowingMetapaths[target].pathWps plutôt que
de lire metapathFollowingCurrentWp, parce que SelectWP s'exécute avant que cet index
ne soit avancé - un test sur l'index serait décalé d'un cran. Passer par le nom couvre
aussi le saut en avant avec MoveToWp et le départ en route inverse, qui posent tous
l'état de la méta-route avant d'appeler SelectWP.
De meilleurs réglages par défaut pour les nouveaux joueurs
Simple : Installer Sku à neuf part désormais d'une configuration utilisable au lieu de
vous laisser trouver l'essentiel d'abord. Quelques touches sont préassignées, la
discussion est répartie de façon sensée, et certaines annonces sont actives dès le
départ - globalement la façon dont les joueurs expérimentés le configurent de toute
manière. Chacun de ces réglages par défaut reste à sa place habituelle dans le menu
et peut être modifié comme toujours. Les profils et personnages existants ne sont pas
touchés : un réglage par défaut ne s'applique que là où rien n'a jamais été
enregistré. Pour récupérer les nouvelles touches, utilisez Assignations de touches
Sku -> tout réinitialiser - attention, cela réinitialise vraiment TOUTES les touches,
y compris celles que vous avez assignées vous-même.
Technique : Modifications de valeurs par défaut uniquement, dans
SkuZOptions/SkuKeyBinds.lua (skuDefaultKeyBindings), SkuChat/Core.lua et Options.lua
(ChatFrameDefaultTabs plus le jeu d'onglets par défaut), SkuCore/Options.lua
(SkuCore.defaults) et SkuCore/aqCombat.lua (aqCombatOnLogin). Les valeurs
enregistrées survivent, parce que chaque endroit n'écrit que sur nil et qu'AceDB ne
remplit les valeurs par défaut que dans les trous. Deux vrais changements de
comportement les accompagnent : le lieur dans SkuNav:CreateSkuNavMain n'appliquait
jamais que .key, si bien qu'une SECONDE touche assignée par l'utilisateur ne faisait
absolument rien pour toutes les actions de navigation (SkuKeyBindsMatchKey l'avait
compris depuis toujours) - il passe maintenant par SkuKeyBindsGetKeys et lie les
deux. Et comme SkuChat émet un message une fois par onglet où le type est activé, un
type de message ayant son propre onglet doit être coupé dans tous les autres onglets,
sinon il est lu deux fois.
Hôtel des ventes : acheter plusieurs fois le même objet fonctionne de nouveau
Simple : Acheter plus d'une enchère du même objet achetait la première puis s'arrêtait
sur « erreur interne des enchères » ; le second achat n'était jamais proposé à
nouveau. Les deux sont corrigés - les achats suivants aboutissent, et le message
d'erreur intempestif du serveur n'est plus lu à voix haute pendant qu'une recherche
est en cours.