Merge remote-tracking branch 'upstream/sylvaniacore'

This commit is contained in:
2026-09-12 16:01:47 +04:00
105 changed files with 13193 additions and 252 deletions
@@ -0,0 +1,97 @@
# -*- coding: utf-8 -*-
"""Genere creature_template_journal pour les boss WoD/Legion sans butin.
Source officielle : JournalInstance -> JournalEncounter -> JournalEncounterCreature."""
import sys, subprocess, collections
sys.path.insert(0, '/home/ubuntu')
from db2read import DB2
D = '/home/ubuntu/server/data/dbc/enUS/'
CIBLES = [1175,1176,1182,1195,1209,1279,1358,1205,1228,
1456,1458,1466,1477,1492,1501,1544,1571,1753,1520,1530,1676]
# adds / decors qui partagent le modele du boss mais ne doivent rien lacher
EXCLUS = {
76057, # Carrion Worm (add de Bonemaw)
76874, 76945, 76884, # Dreadwing / Ironcrusher / Cruelfang (bestioles de Darmac)
77337, # Aknor Steelbringer (add de Flamebender)
76810, # Furnace Engineer
76087, # Defense Construct (Skyreach)
99801, 105383, 105322, 105304, # tentacules
117123, # Tidescale Legionnaire
122571, 125860, 122319, 125340, # adds du Siege du Triumvirat
118460, # Engine of Souls (l'encontre se termine sur Soul Queen Dejahna)
76018, 82556, # Drakonid Monstrosity (trash UBRS)
106482, # Malfurion (PNJ de la rencontre Cenarius)
120996, # doublon d'Atrigan
77810, # doublon de Soulbinder Nyami
84336, # doublon de Yalnu (pre-event)
83893, 83892, # Protecteurs anciens : on garde Dulhu comme porteur
76973, # Hans'gar : on garde Franzok
78237, # Phemos : on garde Pol
116689, # Atrigan : on garde Belac
118523, 118374, # Soeurs de la Lune : on garde Priestess Lunaspyre
102681, 102679, # Dragons du Cauchemar : on garde Lethon
80808, 80805, # Contremaitres du Rail-de-fer : on garde Ahri'ok Dugru
83613, # Koramar : on garde Skulloc
76806, # Haut fourneau : on garde Foreman Feldspar ? non -> voir plus bas
77231, 77477, # Vierges de fer : on garde Admiral Gar'an
}
# Haut fourneau : la rencontre se termine sur le Coeur de la Montagne
EXCLUS.discard(76806); EXCLUS.add(76809)
ji = DB2(D+'JournalInstance.db2', "ssiiiihhbbi", [1]*11, 10, -1)
je = DB2(D+'JournalEncounter.db2', "ssfhhhhbbii", [1,1,2,1,1,1,1,1,1,1,1], -1, -1)
jec = DB2(D+'JournalEncounterCreature.db2',"ssiiihbi", [1]*8, 7, 5)
jei = DB2(D+'JournalEncounterItem.db2', "ihbbbi", [1]*6, 5, 1)
instMap = {ji.getId(i): ji.getRaw(i,6,0,"h") for i in range(ji.recCount)}
encMap = {}
for i in range(je.recCount):
eid = je.getId(i); mid = instMap.get(je.getRaw(i,6,0,"h"), 0)
if mid in CIBLES: encMap[eid] = mid
nbItems = {eid: len(jei.parentMap.get(eid, [])) for eid in encMap}
disp = collections.defaultdict(set)
for eid, idxs in jec.parentMap.items():
if eid in encMap:
for r in idxs: disp[eid].add(jec.getRaw(r,2,0,"i"))
q = ("SELECT DISTINCT c.map, ct.entry, ct.name, ct.rank, ct.ScriptName, "
"ct.modelid1, ct.modelid2, ct.modelid3, ct.modelid4, "
"(SELECT COUNT(*) FROM creature c2 WHERE c2.id=ct.entry AND c2.map=c.map), "
"(SELECT COUNT(*) FROM creature_loot_template l WHERE l.entry=ct.lootid) "
"FROM creature c JOIN creature_template ct ON ct.entry=c.id "
"WHERE c.map IN (%s)" % ','.join(map(str, CIBLES)))
out = subprocess.run(['mysql','-N','-B','dc_world','-e',q], capture_output=True, text=True).stdout
byMapModel = collections.defaultdict(list)
for line in out.splitlines():
f = line.split('\t')
mp, entry, name, rank, script = int(f[0]), int(f[1]), f[2], int(f[3]), f[4]
spawns, butin = int(f[9]), int(f[10])
for m in (int(x) for x in f[5:9]):
if m: byMapModel[(mp, m)].append((entry, name, rank, script, spawns, butin))
rows, rejets = [], []
for eid, mid in sorted(encMap.items(), key=lambda kv: kv[1]):
cands, seen = [], set()
for d in disp.get(eid, ()):
for c in byMapModel.get((mid, d), []):
if c[0] in seen: continue
seen.add(c[0]); cands.append(c)
# on ne garde que les boss credibles, sans butin, hors liste d'exclusion
keep = [c for c in cands
if c[0] not in EXCLUS and c[5] == 0
and (c[3].startswith('boss_') or c[2] == 3 or (c[2] >= 1 and c[4] <= 3))]
if not keep:
rejets.append((mid, eid, nbItems.get(eid,0), [(c[0], c[1], c[5]) for c in cands]))
continue
keep.sort(key=lambda c: (0 if c[3].startswith('boss_') else 1, c[4]))
for c in keep:
rows.append((mid, eid, c[0], c[1], nbItems.get(eid,0)))
print("-- creature_template_journal : %d lignes" % len(rows))
for mid, eid, entry, name, n in rows:
print("(%d, %d), -- carte %d : %s (%d objets au journal)" % (entry, eid, mid, name, n))
print()
print("-- rencontres sans candidat retenu : %d" % len(rejets))
for mid, eid, n, cands in rejets:
print("-- carte %d rencontre %d (%d objets) -> %s" % (mid, eid, n, cands if cands else "aucun PNJ apparu"))
@@ -0,0 +1,25 @@
import sys, subprocess, collections
sys.path.insert(0, '/home/ubuntu')
from db2read import DB2
D = '/home/ubuntu/server/data/dbc/enUS/'
ji = DB2(D+'JournalInstance.db2', "ssiiiihhbbi", [1]*11, 10, -1)
je = DB2(D+'JournalEncounter.db2', "ssfhhhhbbii", [1,1,2,1,1,1,1,1,1,1,1], -1, -1)
jei = DB2(D+'JournalEncounterItem.db2', "ihbbbi", [1]*6, 5, 1)
instMap = {ji.getId(i): ji.getRaw(i,6,0,"h") for i in range(ji.recCount)}
enc = {}
for i in range(je.recCount):
enc[je.getId(i)] = (instMap.get(je.getRaw(i,6,0,"h"),0), je.getRaw(i,9,0,"i"))
rows = subprocess.run(['mysql','-N','-B','dc_world','-e',
"SELECT j.entry, j.JournalEncounterID, ct.name FROM creature_template_journal j "
"JOIN creature_template ct ON ct.entry=j.entry WHERE j.entry>70000"],
capture_output=True, text=True).stdout
print("entry\tenc\tcarte\tordre\tnbObjets\tnom")
bad=0
for line in rows.splitlines():
e, eid, name = line.split('\t'); eid=int(eid)
mid, oi = enc.get(eid, (0,0))
n = len(jei.parentMap.get(eid, []))
flag = "" if (mid and n) else " <<< PROBLEME"
if flag: bad+=1
print("%s\t%d\t%d\t%d\t%d\t%s%s" % (e, eid, mid, oi, n, name, flag))
print("problemes:", bad)
File diff suppressed because it is too large Load Diff
+164
View File
@@ -0,0 +1,164 @@
# Legion — référence de travail
Ce document n'est pas une fiche encyclopédique. C'est un **croisement** : ce que
le jeu officiel contient d'un côté, ce que notre base contient de l'autre. Un
fait non vérifié contre nos données ne sert à rien pour réparer.
Chaque section suit la même règle : la source officielle est citée, puis la
requête qui l'a confrontée à `dc_world`, puis l'écart s'il y en a un.
---
## L'ossature de l'extension
| Correctif | Contenu | Ce qui nous concerne |
|---|---|---|
| **7.0** Legion | Invasion, les quatre zones, les Sanctums de classe, l'arme prodigieuse | Scénario d'introduction du Rivage brisé |
| **7.1** Retour à Karazhan | Suramar, Karazhan | — |
| **7.2** Tombe de Sargeras | **Campagne du Front-de-Légion**, le Rivage brisé jouable, montures de classe | Notre chantier actuel |
| **7.3** Ombres d'Argus | Argus, l'Insurgé | — |
---
## Le piège du Rivage brisé : DEUX scénarios
C'est l'erreur qui m'a coûté le plus de temps, et elle est facile à refaire.
| | Introduction | Assaut |
|---|---|---|
| Correctif | 7.0 | 7.2 |
| Carte | **1460** | **1666** |
| Scénario | 786 | 1280 |
| Récit | La bataille perdue. Varian, Vol'jin, Tirion, Krosus, Gul'dan qui ouvre la Tombe | Le retour offensif depuis Dalaran |
| Entrées de créatures | 90xxx | 116xxx122xxx |
| Script | `scenario_broken_shore_intro` | `scenario_7.2_broken_shore_intro` |
| Quêtes liées | 42740 (Alliance) / 44543 (Horde) | **45102**, **46734** |
Les deux portent le même nom en jeu. Quand une quête du Rivage brisé ne se
débloque pas, **la première question est : laquelle des deux cartes ?**
Les objectifs de 45102 parlent de « flèches de la Légion » (*Legion Spires*) et
de Lord Kalgorath — tous en 116xxx, donc 7.2, donc carte 1666. Rien de tout
cela n'est atteignable sur la 1460.
---
## Campagne du Front-de-Légion (7.2)
Source : [Breaching the Tomb](https://warcraft.wiki.gg/wiki/Breaching_the_Tomb),
qui liste les quinze quêtes de l'exploit du même nom. En jeu officiel elles
étaient **échelonnées par semaines**, du 28 mars au 6 juin 2017 — d'où le
sentiment de campagne décousue.
### État chez nous, au 01/09/2026
Les quinze quêtes existent, **toutes avec donneur et receveur**. La campagne
n'était donc pas cassée : seuls ses deux points d'entrée l'étaient.
| ID | Quête | Donneur | Objectifs |
|---|---|---|---|
| 46730 | Armies of Legionfall | Khadgar **86563** (Dalaran) | 1 |
| 46734 | Assault on Broken Shore | Khadgar 86563 + **116302** | 2 |
| 45102 | Begin the Assault | Khadgar **116302** | 3 |
| 46245 | Begin Construction | Commander Chambers 120183 | 1 |
| 46845 | Vengeance Point | Heidirk the Scalekeeper 120118 | 3 |
| 47137 | Champions of Legionfall | Maiev Shadowsong 116576 | 1 |
| 47139 | Mark of the Sentinax | Illidan Stormrage 117873 | 1 |
| 46252 | Intolerable Infestation | Maiev 116576 | 1 |
| 46250 | Take Out the Head... | Maiev 116576 | 1 |
| 46246 | Strike Them Down | Maiev 116576 | 1 |
| 46832 | Aalgen Point | Commander Chambers 120183 | **0** ⚠ |
| 46247 | Defending Broken Isles | Maiev 116576 | 1 |
| 46251 | Shard Times | Khadgar 116302 | 1 |
| 46248 | Self-Fulfilling Prophecy | Prophet Velen 120372 | 1 |
| 46769 | Relieved of Their Valuables | Khadgar 116302 | 1 |
| 46249 | Championing Our Cause | Khadgar 116302 | 1 |
**Les deux Khadgar.** Ne pas les confondre, ils portent le même nom :
- **86563** — Anneau de Krasus, Dalaran, carte 1220 en `(-841, 4258, 746)`.
L'altitude 746 est celle de la cité flottante. C'est lui qui ouvre la
campagne et qui lance le scénario d'assaut.
- **116302** — Pointe de la Délivrance, sur le Rivage brisé, carte 1220 en
`(-1628, 3194, 130)`. Il donne le reste.
- **120215** — un troisième, posé nulle part chez nous. La référence le place
à l'Anneau de Krasus en phases 7312/7313. C'est la cible du crédit
« Speak to Khadgar » de la quête 46734.
### Deux anomalies relevées
**46832 « Aalgen Point » n'a aucun objectif.** Elle a pourtant donneur et
receveur, et aucune ligne dans `quest_template_addon` — donc pas de
`SpecialFlags = 2` qui justifierait une validation par script. À examiner.
**48641 porte aussi le titre « Armies of Legionfall »**, sans aucun donneur,
avec `QuestSortID 7543` (la zone du Rivage brisé) là où 46730 porte `-428`.
Probablement une variante de suivi. Ne pas la confondre avec la vraie.
---
## Entrer dans le scénario d'assaut
Le déroulement officiel, décrit par les joueurs
([Warcraft Wiki](https://warcraft.wiki.gg/wiki/Assault_on_Broken_Shore_(quest)),
[Icy Veins](https://www.icy-veins.com/forums/topic/28263-patch-72-assault-on-the-broken-shore-scenario/)) :
> On prend la quête auprès de l'archimage à l'Anneau de Krasus, on choisit
> « I am ready to launch the assault », une cinématique se joue, puis
> *you will be flown* jusqu'au scénario.
Trois conséquences pour nous :
1. L'entrée est une **option de dialogue**, pas un portail. Elle n'existait ni
dans notre base ni dans le dump de référence — donnée de capture jamais
importée. Recréée dans `npc_archmage_khadgar_86563`.
2. Le sort **240603** porte la destination officielle dans
`spell_target_position` : carte 1666, `(-1111.6, 2969.71, 186.02)`.
3. « Emporté par la voie des airs », c'est le **corbeau arcanique 118517**,
invoqué par le sort **243303** (effet 28 SUMMON, valeur 118517 — vérifié
dans `SpellEffect.db2` du build 7.3.5.26972). Son chemin de vol 11322708
part de l'altitude de Dalaran (751) et descend au niveau de la mer (4).
---
## Les huit étapes du scénario 1280
Relevées dans `ScenarioStep`, `CriteriaTree` et `Criteria` du build
7.3.5.26972 — pas recopiées d'un autre cœur.
| Ordre | Arbre | Type | Asset | Montant | Étape |
|---|---|---|---|---|---|
| 0 | 56811 | 92 | 56460 | 1 | Into the Fray |
| 1 | 56872 | 92 | 56697 | 3 | Vanguard of the Assault |
| 2 | 56874 | 92 | 56500 | 1 | Might of the Legion (Kalgorath) |
| 3 | 57180 | 92 | 56779 | barre de 200 | Rifts of Chaos |
| 3 | 57185 | **73** | 56778 | 3 | ... les trois portails |
| 4 | 57183 | 92 | 56780 | 1 | The Doomguard's Command (Arganoth) |
| 5 | 57293 | **68** | 267955 | 1 | Gateway to Ruin |
| 6 | 58303 | 92 | 57574 | 7 | Pillar of Fire (7 bombes) |
| 7 | 59170 | 92 | 57579 | 1 | Mephistroth |
Types : **92** `SEND_EVENT_SCENARIO` (alimenté par `DoSendEventScenario`),
**73** `SEND_EVENT`, **68** `USE_GAMEOBJECT` (les deux par `UpdateCriteria`).
Concordance rassurante : la carte porte exactement **7 bombes arcaniques**
(120743) pour un critère qui en exige 7, et 7 portails (118558) dont 3 à
fermer.
---
## Comment étendre ce document
La méthode, pas le contenu, est ce qui compte :
1. Partir d'une source officielle — les wikis Warcraft sont fiables sur les
chaînes de quêtes ; Wowhead ment quand on le lit par `curl`, ses tables
sont en JavaScript.
2. Pour tout ce qui est chiffré — critères, sorts, scénarios — passer par
`wago.tools`, qui sert les DB2 du build **7.3.5.26972**, le nôtre.
Format : `/db2/<Table>/csv?build=7.3.5.26972&filter[Colonne]=valeur`.
Sans le filtre, les grosses tables font expirer la requête.
3. **Toujours confronter à `dc_world`** avant d'écrire quoi que ce soit ici.
Une ligne qui n'a pas été vérifiée contre notre base n'a pas sa place.
4. Noter les écarts, pas les conformités. Ce qui marche n'a pas besoin d'être
documenté.
@@ -0,0 +1,114 @@
# La bataille du Rivage brisé — déroulé de référence (carte 1460, scénario 786)
Ce document croise trois sources, et dit toujours laquelle parle :
- **la vidéo** fournie par l'exploitant (40 min, écran partagé Alliance/Horde),
qui donne l'ordre réel des événements, le minutage et les compteurs affichés ;
- **les DB2 du build 7.3.5.26972** (`ScenarioStep`, `CriteriaTree`, `Criteria`),
qui donnent les critères chiffrés ;
- **[Warcraft Wiki](https://warcraft.wiki.gg/wiki/The_Battle_for_Broken_Shore_(quest))**,
qui donne les intitulés et les répliques.
Ce que **notre base** contient est indiqué en regard, avec l'écart quand il y en a.
---
## Les neuf étapes
| # | Alliance | Horde | Critère officiel |
|---|---|---|---|
| 1 | The Broken Shore | idem | arbre 42768 — asset 44060 ×1 |
| 2 | Storm The Beach | idem | 44095 ×**33**, 52643 ×**3**, 44077 ×**3** |
| 3 | Defeat the Commander — **Arganoth** | Defeat the Commander — **Azgalor** | arbre 43554 — asset 45131 ×1 |
| 4 | **Find Varian** | **Find The Others** (Sylvanas *et* Baine) | arbre 43589 — asset 45228 ×1 |
| 5 | Destroy the Portal — **4 ancres blindées** | idem | arbre 43415 — asset 45288 ×**4** |
| 6 | Raze the Black City — barre 0→100 % | idem | arbre 42770 — 44384 ×1, 53062 ×2, 53063 ×5, 53064 ×10 |
| 7 | The Highlord — atteindre Tirion | idem | arbre 42772 |
| 8 | Krosus | idem | arbre 43765 |
| 9 | **Stop Gul'dan** | **Hold the Ridge** | arbre 47225 |
---
## Trois erreurs que ce croisement a révélées
**L'étape 5 comptait la mauvaise créature.** Notre script invoquait deux
« ancres dimensionnelles » (90637) de sa fabrication. Le jeu demande la
destruction de **quatre ancres blindées** (`101667`), dont la carte porte déjà
quinze exemplaires autour de `(1107, 2061)`. Corrigé.
**L'étape 3 n'a pas le même adversaire selon la faction.** Alliance : *Dread
Commander Arganoth*. Horde : *Fel Commander Azgalor*. Notre script ne connaît
qu'Arganoth et l'invoque pour les deux camps. **Non corrigé.**
**L'étape 4 non plus.** Alliance : trouver Varian. Horde : trouver **Sylvanas
et Baine**, deux personnages, pas Vol'jin. Notre script cherche Vol'jin côté
Horde. **Non corrigé.**
Et l'étape 9 diverge aussi : l'Alliance arrête Gul'dan, la Horde tient la
crête pendant ce temps.
---
## Nos dialogues sont inventés
Toutes les répliques de `creature_text` pour ce scénario ont un
`BroadcastTextId` à **zéro** : elles ont été écrites à la main, elles ne
viennent pas du jeu. Elles sont plausibles mais fausses, et l'une d'elles a
directement produit un bug.
| Entrée | Notre texte | Le jeu |
|---|---|---|
| 90714 Jaina | « Détruisez les ancres **dimensionnelles** » | *Those crystals appear to be anchoring the structures to our dimension. Take them out!* |
| 90713 Varian | « Soldats de l'Alliance ! La Légion s'abat… » | *Genn, Jaina, it's good to see you safe.* |
| 90717 Genn | — | *Just in time. We haven't been able to break their line. Now we may have a chance.* |
| 90708 Vol'jin | « Fils et filles de la Horde ! … » | *Dere's only one way offa dis beach, and it be t'rough alla dose demons ahead of us.* |
| 90413 Gul'dan | « Vous arrivez trop tard ! … » | *I have seen the end of your pitiful world, Wrynn. … The legion is ENDLESS.* |
C'est la réplique de Jaina qui a fabriqué l'erreur de l'étape 5 : le script a
été écrit d'après elle. **La donnée fausse produit du code faux.**
---
## Déroulé relevé sur la vidéo
Minutage de la vidéo, pas du scénario — les premières minutes sont à Dalaran.
| Temps | Événement |
|---|---|
| 06:02 | Jaina : « Attention, des infernaux ! » — début du combat de plage |
| 06:04 | Le commandant de l'effroi Arganoth parle |
| 06:54 | Genn Grisetête : « Celui-là est à moi ! » |
| 07:00 | Jaina : « Est-ce que tout le monde va bien ? » |
| 07:26 | Genn : « Les troupes de Varian ont dû arriver de l'autre côté de… » |
| 07:48 | Jaina : « Je n'avais jamais vu des démons progresser… » |
| 08:18 | Genn : « Quelque chose a changé. La Légion n'est… » |
| 08:38 | Jaina : « Oh, non… » |
| 08:46 | Varian : « Tenez bon, soldats de l'Alliance ! » |
| 09:12 | Varian : « Genn, Jaina, je suis heureux de voir que vous allez bien. » |
| 09:18 | Varian : « En formation, protecteurs de l'Alliance ! Repoussons-les ! » |
| 17:28 | Varian : « Vous avez une autre occasion de l'abattre, Sylvanas » |
| 17:32 | Sylvanas : « C'est comme si c'était fait, Wrynn. » |
| 17:54 | Varian : « Tout le monde, de l'autre côté du gouffre ! » |
| 18:08 | Sylvanas : « Nous allons prendre la crête et couvrir votre… » |
| 18:44 | Gul'dan : « M'échapper ? C'est plutôt vous qui n'échapperez pas à mon… » |
| ~08:20 vidéo | phase 5 : compteur **0/4 → 2/4 → 3/4 Ancres blindées détruites** |
| ~09:00 vidéo | phase 6 : barre **0 % → 2 % → 13 % → 37 % → 63 %** |
Le texte ci-dessus vient de la reconnaissance optique sur une vidéo 720p en
écran partagé : **les fins de phrase sont tronquées** par la largeur de la
fenêtre de discussion, et certains caractères sont incertains. Il donne
l'ordre et l'orateur de façon fiable ; il ne fait pas autorité sur la
formulation exacte. Pour celle-ci, se référer au wiki.
---
## Ce qui reste à faire
1. **Le commandant Horde** : invoquer Azgalor et non Arganoth.
2. **L'étape 4 Horde** : deux cibles, Sylvanas et Baine.
3. **L'étape 9 Horde** : tenir la crête, pas arrêter Gul'dan.
4. **Les étapes 6 à 9** avancent encore par forçage, sans leurs critères. La
barre de 300 points de l'étape 6 est alimentée par quatre événements de
poids différents (1, 2, 5, 10) dont le sens reste à établir.
5. **Remplacer les dialogues inventés** par les vrais. Ils n'existent qu'en
anglais dans nos sources : traduire, ou assumer l'anglais.
+50 -9
View File
@@ -1,6 +1,7 @@
# Chantier — Assaut du Rivage brisé (carte 1666)
État au 27/08/2026. **Rien n'est appliqué au serveur à ce stade.**
État au 31/08/2026. **Tout est appliqué et déployé.** Rien n'est vérifié
en jeu : c'est la seule chose qui manque.
## Le blocage d'origine
@@ -70,12 +71,52 @@ C'est un contrôle de cohérence — vérifier que le parseur trouvait bien des
lignes sur des cartes connues — qui a révélé le bug. **Toujours valider un
extracteur sur un cas positif connu avant de croire un résultat négatif.**
## Reste à faire
## Ce qui a été fait le 31/08
1. Extraire et transposer les 593 + 59 placements.
2. Extraire les chemins des 180 créatures mobiles.
3. Transposer la déclaration du scénario.
4. Porter les 808 lignes de C++ et les adapter à notre API, qui a divergé
(`MoveJump`, `SetFacingTo`, `GetEffect` diffèrent déjà — constaté ailleurs).
5. Raccorder le nom de script.
6. Refaire autrement le comportement des 12 créatures à `AiID`.
1. **593 créatures, 59 objets, 1495 points** appliqués sur la carte 1666.
Le fichier était généré depuis le 27/08 et dormait sans être exécuté.
2. **808 lignes de C++ portées** dans
`src/server/scripts/BrokenIsles/Scenario/scenario_assault_broken_shore.cpp`,
enregistré sous `scenario_7.2_broken_shore_intro` — le nom que
`instance_template` réclamait déjà, donc aucune modification de base.
Ce nom contient un point : la classe est écrite à la main, la macro
`RegisterInstanceScript` ne l'accepterait pas.
3. **Les 5 scripts rattachés** à leurs 11 entrées, toutes vérifiées
exclusives à la carte 1666.
4. **87 points des 5 chemins scriptés** extraits et posés.
5. **Le donneur de la quête 45102** raccordé : Khadgar 116302.
## Le piège de l'extraction, deuxième épisode
Les cinq chemins scriptés (11322705 à 11322709) ne sont **pas** dans
`waypoint_data` mais dans **`waypoint_data_script`**, une table que notre
schéma n'a pas — notre `sWaypointMgr` ne lit que la première. L'extraction
du 27/08 ne regardait que celle-là et les a donc silencieusement manqués.
Conséquence si on ne l'avait pas vu : le corbeau arcanique n'aurait jamais
décollé, et comme c'est l'atteinte du point 20 de son vol qui déclenche
l'étape 0, **le scénario n'aurait jamais démarré** — sans la moindre
erreur dans le journal.
C'est la même leçon qu'en août, sous une autre forme : ce n'est pas
l'extracteur qui a menti, c'est la table qu'on n'a pas pensé à ouvrir.
## Ce qui reste
1. **Tout vérifier en jeu.** Rien n'a été joué. Prendre 45102 auprès de
Khadgar 116302 en Îles brisées et dérouler le scénario.
2. **Les crédits 116253 et 116279** sont câblés sur la fermeture des deux
premiers portails **par déduction** : la quête parle de « First » et
« Second Legion Spire destroyed », et les portails sont les seuls
objets destructibles de l'étape 3. À confirmer ou corriger.
3. **Les 12 créatures à `AiID = 7424`** (119422 ×9, 119425 ×2, 119412 ×1)
gardent un comportement de mêlée ordinaire. Vérifié : le dump ne
contient **aucune** table définissant les `AiID` — ce comportement
n'est pas récupérable depuis cette source. Ne pas rouvrir ce point
sans une autre référence.
4. **Khadgar 120215** n'est posé nulle part chez nous. La référence le
place deux fois sur la carte 1220, en phases 7312 et 7313. Le crédit
« Speak to Khadgar » est pour l'instant accordé par le script à la fin
du scénario ; c'est un raccourci assumé, à arbitrer.
5. **Concordance rassurante** relevée au passage : 7 bombes arcaniques
posées sur la carte, pour un critère qui en exige exactement 7.
@@ -0,0 +1,98 @@
# -*- coding: utf-8 -*-
"""Extrait les 5 chemins scriptes du Rivage brise (carte 1666).
Ils vivent dans `waypoint_data_script`, une table que notre coeur n'a
pas : notre sWaypointMgr ne lit que `waypoint_data`. C'est pourquoi
l'extraction precedente, qui ne regardait que cette derniere, les avait
manques -- et pourquoi les alliés comme le corbeau restaient immobiles.
Correspondance des colonnes, pilotee par les NOMS :
reference nous
id, point, position_x/y/z, identiques
orientation, delay,
action, action_chance
move_flag -> move_type (voir ci-dessous)
speed, entry -> ABANDONNEES, aucun equivalent
wpguid -> 0
move_flag vaut 1 sur la totalite des 87 points des cinq chemins. Notre
enumeration donne 0=marche, 1=course, 2=atterrissage, 3=decollage : la
valeur 1 designe la course dans les deux. Le vol du corbeau ne vient pas
du chemin mais de SetFlyMode et du vehicule.
CONTROLE : les effectifs attendus sont ecrits en dur et verifies. Un
extracteur qui renvoie zero sans broncher est le piege deja rencontre
sur ce chantier.
"""
import io, re, sys
DUMP = '/home/ubuntu/tmp/abs/LegionCore_world_2020_04_25.sql'
ATTENDU = {11322705: 24, 11322706: 18, 11322707: 23, 11322708: 21, 11322709: 1}
# colonnes de waypoint_data_script, relevees dans l'entete de l'INSERT
COLS = ['id','point','position_x','position_y','position_z','orientation',
'delay','move_flag','speed','action','action_chance','entry','wpguid']
motif = re.compile(r'^\((\d+),(.*)\)[,;]\s*$')
trouves = {k: [] for k in ATTENDU}
with io.open(DUMP, encoding='utf-8', errors='surrogateescape') as f:
for ligne in f:
m = motif.match(ligne)
if not m:
continue
ident = int(m.group(1))
if ident not in ATTENDU:
continue
champs = [ident] + [c.strip() for c in m.group(2).split(',')]
if len(champs) != len(COLS):
sys.exit('ERREUR : %d champs pour le chemin %d, %d attendus'
% (len(champs), ident, len(COLS)))
trouves[ident].append(dict(zip(COLS, champs)))
for ident, n in sorted(ATTENDU.items()):
reel = len(trouves[ident])
if reel != n:
sys.exit('ERREUR : chemin %d -> %d points extraits, %d attendus.'
% (ident, reel, n))
total = sum(len(v) for v in trouves.values())
L = []
L.append('-- ' + '=' * 69)
L.append("-- Assaut du Rivage brisé (carte 1666) — les 5 chemins scriptés")
L.append('--')
L.append("-- Ces chemins vivent dans `waypoint_data_script` chez la référence,")
L.append("-- une table que notre cœur n'a pas : sWaypointMgr ne lit que")
L.append("-- `waypoint_data`. L'extraction du 27/08 ne regardait que cette")
L.append("-- dernière et les avait donc manqués — d'où des alliés immobiles et,")
L.append("-- plus grave, un corbeau arcanique qui ne décollait jamais.")
L.append('--')
L.append("-- Le 11322708 est le vol d'arrivée : c'est l'atteinte de son point 20")
L.append("-- qui déclenche l'étape 0 du scénario. Sans lui, rien ne démarre.")
L.append('--')
L.append("-- move_flag → move_type : la valeur est 1 sur les %d points." % total)
L.append("-- Notre énumération donne 0=marche, 1=course, 2=atterrissage,")
L.append("-- 3=décollage ; la course correspond. `speed` et `entry` n'ont")
L.append("-- aucun équivalent chez nous et sont abandonnées.")
L.append('--')
for ident, n in sorted(ATTENDU.items()):
L.append('-- %d : %2d points' % (ident, n))
L.append('-- ' + '=' * 69)
L.append('')
L.append('DELETE FROM `waypoint_data` WHERE `id` BETWEEN 11322705 AND 11322709;')
L.append('')
L.append('INSERT INTO `waypoint_data` (`id`,`point`,`position_x`,`position_y`,'
'`position_z`,`orientation`,`delay`,`move_type`,`action`,`action_chance`,`wpguid`) VALUES')
lignes = []
for ident in sorted(ATTENDU):
for r in sorted(trouves[ident], key=lambda x: int(x['point'])):
lignes.append('(%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,0)' % (
r['id'], r['point'], r['position_x'], r['position_y'], r['position_z'],
r['orientation'], r['delay'], r['move_flag'], r['action'], r['action_chance']))
L.append(',\n'.join(lignes) + ';')
io.open('/tmp/rivage_1666_chemins.sql', 'w', encoding='utf-8').write('\n'.join(L) + '\n')
print('%d points extraits sur %d chemins, controles conformes' % (total, len(ATTENDU)))
+26
View File
@@ -0,0 +1,26 @@
-- =====================================================================
-- Aubergiste du port de Hurlevent — phase manquante
--
-- Spawn cree en jeu par `.npc add 6727` (Innkeeper Brianna) a
-- (-8296.06, 1388.89, 4.95), aire 4411 « Port de Hurlevent ».
--
-- La commande a enregistre la ligne SANS phase, alors que `phase_area`
-- place tout joueur de cette aire en 6666 « Pre-Broken Shore Stormwind
-- Harbor » (et 13306 pour la rampe). PhaseShift::CanSee exigeant une
-- intersection, la creature etait invisible a tout joueur normal, et
-- visible en mode MJ seulement -- SetAlwaysVisible court-circuitant le
-- test de phase.
--
-- La cause est corrigee dans le core (cs_npc.cpp) : les prochains
-- `.npc add` conserveront la phase. Cette ligne repare le spawn deja
-- pose, pour ne pas avoir a le recreer.
--
-- 6666 et non 13306 : l'aubergiste est sur le quai, pas sur la rampe,
-- et c'est la phase que portent les 371 autres creatures de cette aire.
-- =====================================================================
UPDATE `creature`
SET `PhaseId` = 6666
WHERE `guid` = 290200759
AND `id` = 6727
AND `PhaseId` = 0;
@@ -0,0 +1,123 @@
-- =====================================================================
-- Mascottes de combat : « On The Mend » / soin chez le maitre d'ecurie
-- Signalement Discord : Murog (Orgrimmar) ouvre directement la boutique
-- au lieu du dialogue, la quete 31589 est donc impossible a valider.
--
-- Cause : les maitres d'ecurie n'exposent pas (ou n'implementent pas)
-- l'option de gossip « I'd like to heal and revive my battle pets. ».
-- * Murog (47764) et Jaelysia (10085) n'ont aucun gossip_menu_id :
-- ils retombent sur le menu 0 par defaut, dont la seule option
-- compatible pour un non-chasseur est « vendeur » -> le client 7.3.5
-- selectionne automatiquement l'unique option et ouvre la boutique.
-- * Le menu 9821 (menu standard des maitres d'ecurie) a perdu ses
-- options « vendeur » et « ecurie » : seule l'option de soin restait.
-- * Seuls 2 maitres d'ecurie sur 12 avaient le SmartAI qui soigne les
-- mascottes et donne le credit de quete.
-- * Le credit de Jenova Stoneshield (Hurlevent) pointait sur le credit
-- d'Elwynn (64320) au lieu de celui de Hurlevent (65214).
--
-- Applique le 2026-09-10. Rollback : battlepet_stablemaster_heal_ROLLBACK.sql
-- =====================================================================
-- ---------------------------------------------------------------------
-- 1. Menu 9821 : restaurer les options vendeur et ecurie
-- (l'option 2 « heal and revive my battle pets » existe deja)
-- ---------------------------------------------------------------------
DELETE FROM `gossip_menu_option` WHERE `MenuId`=9821 AND `OptionIndex` IN (0,4);
INSERT INTO `gossip_menu_option`
(`MenuId`,`OptionIndex`,`OptionIcon`,`OptionText`,`OptionBroadcastTextId`,`OptionType`,`OptionNpcFlag`,`VerifiedBuild`) VALUES
(9821, 0, 0, 'I\'d like to stable my pet here.', 8912, 14, 4194304, 0),
(9821, 4, 1, 'I want to browse your goods.', 3370, 3, 128, 0);
-- ---------------------------------------------------------------------
-- 2. Menu 12106 (Bezzil, maitre d'ecurie gobelin) : meme jeu d'options
-- ---------------------------------------------------------------------
UPDATE `gossip_menu_option` SET `OptionType`=14, `OptionNpcFlag`=4194304
WHERE `MenuId`=12106 AND `OptionIndex`=0;
DELETE FROM `gossip_menu_option` WHERE `MenuId`=12106 AND `OptionIndex` IN (2,3);
INSERT INTO `gossip_menu_option`
(`MenuId`,`OptionIndex`,`OptionIcon`,`OptionText`,`OptionBroadcastTextId`,`OptionType`,`OptionNpcFlag`,`VerifiedBuild`) VALUES
(12106, 2, 0, 'I\'d like to heal and revive my battle pets.', 64115, 1, 1, 0),
(12106, 3, 1, 'I want to browse your goods.', 3370, 3, 128, 0);
-- ---------------------------------------------------------------------
-- 3. PNJ mal configures
-- ---------------------------------------------------------------------
-- Murog (Orgrimmar) et Jaelysia (Sombrivage) : aucun menu de gossip
UPDATE `creature_template` SET `gossip_menu_id`=9821 WHERE `entry` IN (47764,10085);
-- Seriadne (Teldrassil) : npcflag ampute de GOSSIP et STABLEMASTER
UPDATE `creature_template` SET `npcflag`=`npcflag`|4194305 WHERE `entry`=10051;
-- ---------------------------------------------------------------------
-- 4. SmartAI : soigner + ressusciter les mascottes et donner le credit
-- ---------------------------------------------------------------------
UPDATE `creature_template` SET `AIName`='SmartAI'
WHERE `entry` IN (6749,9980,9987,10050,10051,10085,16185,17485,45789,47764);
-- Correction du credit de Jenova Stoneshield : Hurlevent (65214), pas Elwynn
UPDATE `smart_scripts` SET `action_param1`=65214, `comment`='Stable Master - Link - Quest Credit 31592'
WHERE `source_type`=0 AND `entryorguid`=11069 AND `id`=2;
DELETE FROM `smart_scripts`
WHERE `source_type`=0 AND `entryorguid` IN (6749,9980,9987,10050,10051,10085,16185,17485,45789,47764);
INSERT INTO `smart_scripts`
(`entryorguid`,`source_type`,`id`,`link`,`event_type`,`event_phase_mask`,`event_chance`,`event_flags`,
`event_param1`,`event_param2`,`event_param3`,`event_param4`,`event_param5`,`event_param_string`,
`action_type`,`action_param1`,`action_param2`,`action_param3`,`action_param4`,`action_param5`,`action_param6`,
`target_type`,`target_param1`,`target_param2`,`target_param3`,`target_x`,`target_y`,`target_z`,`target_o`,`comment`) VALUES
-- Erma (Foret d'Elwynn) - quete 31309
( 6749,0,0,1,62,0,100,0, 9821,2,0,0,0,'', 85,125801,2,0,0,0,0, 7,0,0,0,0,0,0,0,'Erma - Gossip Select - Cast Heal Battle Pets'),
( 6749,0,1,2,61,0,100,0, 0,0,0,0,0,'', 85,133994,2,0,0,0,0, 7,0,0,0,0,0,0,0,'Erma - Link - Cast Revive Battle Pets'),
( 6749,0,2,3,61,0,100,0, 0,0,0,0,0,'', 33, 64320,0,0,0,0,0, 7,0,0,0,0,0,0,0,'Erma - Link - Quest Credit 31309'),
( 6749,0,3,0,61,0,100,0, 0,0,0,0,0,'', 72, 0,0,0,0,0,0, 7,0,0,0,0,0,0,0,'Erma - Link - Close Gossip'),
-- Shelby Stoneflint (Dun Morogh) - quete 31549
( 9980,0,0,1,62,0,100,0, 9821,2,0,0,0,'', 85,125801,2,0,0,0,0, 7,0,0,0,0,0,0,0,'Shelby Stoneflint - Gossip Select - Cast Heal Battle Pets'),
( 9980,0,1,2,61,0,100,0, 0,0,0,0,0,'', 85,133994,2,0,0,0,0, 7,0,0,0,0,0,0,0,'Shelby Stoneflint - Link - Cast Revive Battle Pets'),
( 9980,0,2,3,61,0,100,0, 0,0,0,0,0,'', 33, 65020,0,0,0,0,0, 7,0,0,0,0,0,0,0,'Shelby Stoneflint - Link - Quest Credit 31549'),
( 9980,0,3,0,61,0,100,0, 0,0,0,0,0,'', 72, 0,0,0,0,0,0, 7,0,0,0,0,0,0,0,'Shelby Stoneflint - Link - Close Gossip'),
-- Shoja'my (Durotar) - quete 31572
( 9987,0,0,1,62,0,100,0, 9821,2,0,0,0,'', 85,125801,2,0,0,0,0, 7,0,0,0,0,0,0,0,'Shoja\'my - Gossip Select - Cast Heal Battle Pets'),
( 9987,0,1,2,61,0,100,0, 0,0,0,0,0,'', 85,133994,2,0,0,0,0, 7,0,0,0,0,0,0,0,'Shoja\'my - Link - Cast Revive Battle Pets'),
( 9987,0,2,3,61,0,100,0, 0,0,0,0,0,'', 33, 65180,0,0,0,0,0, 7,0,0,0,0,0,0,0,'Shoja\'my - Link - Quest Credit 31572'),
( 9987,0,3,0,61,0,100,0, 0,0,0,0,0,'', 72, 0,0,0,0,0,0, 7,0,0,0,0,0,0,0,'Shoja\'my - Link - Close Gossip'),
-- Seikwa (Mulgore) - quete 31574
(10050,0,0,1,62,0,100,0, 9821,2,0,0,0,'', 85,125801,2,0,0,0,0, 7,0,0,0,0,0,0,0,'Seikwa - Gossip Select - Cast Heal Battle Pets'),
(10050,0,1,2,61,0,100,0, 0,0,0,0,0,'', 85,133994,2,0,0,0,0, 7,0,0,0,0,0,0,0,'Seikwa - Link - Cast Revive Battle Pets'),
(10050,0,2,3,61,0,100,0, 0,0,0,0,0,'', 33, 65184,0,0,0,0,0, 7,0,0,0,0,0,0,0,'Seikwa - Link - Quest Credit 31574'),
(10050,0,3,0,61,0,100,0, 0,0,0,0,0,'', 72, 0,0,0,0,0,0, 7,0,0,0,0,0,0,0,'Seikwa - Link - Close Gossip'),
-- Seriadne (Teldrassil) - quetes 31553 / 31554
(10051,0,0,1,62,0,100,0, 9821,2,0,0,0,'', 85,125801,2,0,0,0,0, 7,0,0,0,0,0,0,0,'Seriadne - Gossip Select - Cast Heal Battle Pets'),
(10051,0,1,2,61,0,100,0, 0,0,0,0,0,'', 85,133994,2,0,0,0,0, 7,0,0,0,0,0,0,0,'Seriadne - Link - Cast Revive Battle Pets'),
(10051,0,2,3,61,0,100,0, 0,0,0,0,0,'', 33, 65041,0,0,0,0,0, 7,0,0,0,0,0,0,0,'Seriadne - Link - Quest Credit 31553/31554'),
(10051,0,3,0,61,0,100,0, 0,0,0,0,0,'', 72, 0,0,0,0,0,0, 7,0,0,0,0,0,0,0,'Seriadne - Link - Close Gossip'),
-- Jaelysia (Sombrivage) - quete 31583
(10085,0,0,1,62,0,100,0, 9821,2,0,0,0,'', 85,125801,2,0,0,0,0, 7,0,0,0,0,0,0,0,'Jaelysia - Gossip Select - Cast Heal Battle Pets'),
(10085,0,1,2,61,0,100,0, 0,0,0,0,0,'', 85,133994,2,0,0,0,0, 7,0,0,0,0,0,0,0,'Jaelysia - Link - Cast Revive Battle Pets'),
(10085,0,2,3,61,0,100,0, 0,0,0,0,0,'', 33, 65199,0,0,0,0,0, 7,0,0,0,0,0,0,0,'Jaelysia - Link - Quest Credit 31583'),
(10085,0,3,0,61,0,100,0, 0,0,0,0,0,'', 72, 0,0,0,0,0,0, 7,0,0,0,0,0,0,0,'Jaelysia - Link - Close Gossip'),
-- Anathos (Bois des Chants eternels) - quete 31580
(16185,0,0,1,62,0,100,0, 9821,2,0,0,0,'', 85,125801,2,0,0,0,0, 7,0,0,0,0,0,0,0,'Anathos - Gossip Select - Cast Heal Battle Pets'),
(16185,0,1,2,61,0,100,0, 0,0,0,0,0,'', 85,133994,2,0,0,0,0, 7,0,0,0,0,0,0,0,'Anathos - Link - Cast Revive Battle Pets'),
(16185,0,2,3,61,0,100,0, 0,0,0,0,0,'', 33, 65198,0,0,0,0,0, 7,0,0,0,0,0,0,0,'Anathos - Link - Quest Credit 31580'),
(16185,0,3,0,61,0,100,0, 0,0,0,0,0,'', 72, 0,0,0,0,0,0, 7,0,0,0,0,0,0,0,'Anathos - Link - Close Gossip'),
-- Esbina (Azuremyst) - quete 31568
(17485,0,0,1,62,0,100,0, 9821,2,0,0,0,'', 85,125801,2,0,0,0,0, 7,0,0,0,0,0,0,0,'Esbina - Gossip Select - Cast Heal Battle Pets'),
(17485,0,1,2,61,0,100,0, 0,0,0,0,0,'', 85,133994,2,0,0,0,0, 7,0,0,0,0,0,0,0,'Esbina - Link - Cast Revive Battle Pets'),
(17485,0,2,3,61,0,100,0, 0,0,0,0,0,'', 33, 65179,0,0,0,0,0, 7,0,0,0,0,0,0,0,'Esbina - Link - Quest Credit 31568'),
(17485,0,3,0,61,0,100,0, 0,0,0,0,0,'', 72, 0,0,0,0,0,0, 7,0,0,0,0,0,0,0,'Esbina - Link - Close Gossip'),
-- Bezzil (Orgrimmar, gobelins) - quete 31586 - menu 12106
(45789,0,0,1,62,0,100,0,12106,2,0,0,0,'', 85,125801,2,0,0,0,0, 7,0,0,0,0,0,0,0,'Bezzil - Gossip Select - Cast Heal Battle Pets'),
(45789,0,1,2,61,0,100,0, 0,0,0,0,0,'', 85,133994,2,0,0,0,0, 7,0,0,0,0,0,0,0,'Bezzil - Link - Cast Revive Battle Pets'),
(45789,0,2,3,61,0,100,0, 0,0,0,0,0,'', 33, 65200,0,0,0,0,0, 7,0,0,0,0,0,0,0,'Bezzil - Link - Quest Credit 31586'),
(45789,0,3,0,61,0,100,0, 0,0,0,0,0,'', 72, 0,0,0,0,0,0, 7,0,0,0,0,0,0,0,'Bezzil - Link - Close Gossip'),
-- Murog (Orgrimmar) - quete 31589 - le bug signale
(47764,0,0,1,62,0,100,0, 9821,2,0,0,0,'', 85,125801,2,0,0,0,0, 7,0,0,0,0,0,0,0,'Murog - Gossip Select - Cast Heal Battle Pets'),
(47764,0,1,2,61,0,100,0, 0,0,0,0,0,'', 85,133994,2,0,0,0,0, 7,0,0,0,0,0,0,0,'Murog - Link - Cast Revive Battle Pets'),
(47764,0,2,3,61,0,100,0, 0,0,0,0,0,'', 33, 65212,0,0,0,0,0, 7,0,0,0,0,0,0,0,'Murog - Link - Quest Credit 31589'),
(47764,0,3,0,61,0,100,0, 0,0,0,0,0,'', 72, 0,0,0,0,0,0, 7,0,0,0,0,0,0,0,'Murog - Link - Close Gossip');
-- ---------------------------------------------------------------------
-- 5. Menu 12106 : brancher les textes diffuses pour avoir la VF
-- ---------------------------------------------------------------------
UPDATE `gossip_menu_option` SET `OptionBroadcastTextId`= 8912 WHERE `MenuId`=12106 AND `OptionIndex`=0;
UPDATE `gossip_menu_option` SET `OptionBroadcastTextId`=56613 WHERE `MenuId`=12106 AND `OptionIndex`=1;
@@ -0,0 +1,14 @@
-- Rollback de battlepet_stablemaster_heal.sql (2026-09-10)
DELETE FROM `gossip_menu_option` WHERE `MenuId`=9821 AND `OptionIndex` IN (0,4);
DELETE FROM `gossip_menu_option` WHERE `MenuId`=12106 AND `OptionIndex` IN (2,3);
UPDATE `gossip_menu_option` SET `OptionType`=1, `OptionNpcFlag`=1 WHERE `MenuId`=12106 AND `OptionIndex`=0;
UPDATE `creature_template` SET `gossip_menu_id`=0 WHERE `entry` IN (47764,10085);
UPDATE `creature_template` SET `npcflag`=130 WHERE `entry`=10051;
UPDATE `creature_template` SET `AIName`=''
WHERE `entry` IN (6749,9980,9987,10050,10051,10085,16185,17485,45789,47764);
DELETE FROM `smart_scripts`
WHERE `source_type`=0 AND `entryorguid` IN (6749,9980,9987,10050,10051,10085,16185,17485,45789,47764);
UPDATE `smart_scripts` SET `action_param1`=64320, `comment`='Stable Master - Link - Quest Credit 31309'
WHERE `source_type`=0 AND `entryorguid`=11069 AND `id`=2;
@@ -0,0 +1,40 @@
-- =====================================================================
-- OPTIONNEL (non applique le 2026-09-10) - a valider avant execution.
--
-- Complement de battlepet_stablemaster_heal.sql : rend l'option
-- « J'aimerais soigner et ressusciter mes mascottes de combat » reellement
-- fonctionnelle chez TOUS les maitres d'ecurie du menu 9821, et pas
-- seulement chez les 12 PNJ concernes par la quete « On The Mend ».
--
-- Sans ce complement, ~89 maitres d'ecurie affichent l'option mais ne
-- font rien quand on la choisit (bug present avant le correctif).
--
-- Portee : uniquement les PNJ qui ont deja le drapeau STABLEMASTER, le
-- menu 9821, et AUCUNE IA (AIName et ScriptName vides) - donc aucun
-- script existant n'est ecrase. Trois maitres d'ecurie deja en SmartAI
-- (Wesley 9978, Maluressian 10052, Thomas Partridge 33854) sont exclus :
-- il faudrait leur ajouter les lignes avec des `id` libres.
-- =====================================================================
CREATE TEMPORARY TABLE `_sm_heal` AS
SELECT `entry` FROM `creature_template`
WHERE `gossip_menu_id`=9821 AND `npcflag` & 4194304
AND `AIName`='' AND `ScriptName`='';
UPDATE `creature_template` SET `AIName`='SmartAI'
WHERE `entry` IN (SELECT `entry` FROM `_sm_heal`);
DELETE FROM `smart_scripts` WHERE `source_type`=0 AND `entryorguid` IN (SELECT `entry` FROM `_sm_heal`);
INSERT INTO `smart_scripts`
(`entryorguid`,`source_type`,`id`,`link`,`event_type`,`event_phase_mask`,`event_chance`,`event_flags`,
`event_param1`,`event_param2`,`event_param3`,`event_param4`,`event_param5`,`event_param_string`,
`action_type`,`action_param1`,`action_param2`,`action_param3`,`action_param4`,`action_param5`,`action_param6`,
`target_type`,`target_param1`,`target_param2`,`target_param3`,`target_x`,`target_y`,`target_z`,`target_o`,`comment`)
SELECT `entry`,0,0,1,62,0,100,0,9821,2,0,0,0,'',85,125801,2,0,0,0,0,7,0,0,0,0,0,0,0,'Stable Master - Gossip Select - Cast Heal Battle Pets' FROM `_sm_heal`
UNION ALL
SELECT `entry`,0,1,2,61,0,100,0, 0,0,0,0,0,'',85,133994,2,0,0,0,0,7,0,0,0,0,0,0,0,'Stable Master - Link - Cast Revive Battle Pets' FROM `_sm_heal`
UNION ALL
SELECT `entry`,0,2,0,61,0,100,0, 0,0,0,0,0,'',72, 0,0,0,0,0,0,7,0,0,0,0,0,0,0,'Stable Master - Link - Close Gossip' FROM `_sm_heal`;
DROP TEMPORARY TABLE `_sm_heal`;
+91
View File
@@ -0,0 +1,91 @@
-- =====================================================================
-- Butin des donjons et raids WoD / Legion
--
-- Signalement Discord : « les instances HL ne loot pas de stuff ».
-- Cause : sur ce core, le butin d'equipement des boss modernes ne vient
-- PAS de `creature_loot_template` mais du Guide d'aventure de Blizzard :
-- `Unit::Kill` lit `creature_template_journal` (entree -> JournalEncounterID)
-- puis tire 2 objets au hasard dans JournalEncounterItem.db2, avec le
-- masque de difficulte et le contexte d'objet corrects.
-- Or `creature_template_journal` ne contenait que 45 lignes, toutes
-- Burning Crusade : aucun boss de Draenor ni de Legion n'y figurait,
-- et ces boss ont `lootid = 0` -> ils ne lachaient rigoureusement rien.
--
-- Correspondances etablies a partir des DB2 officiels du client :
-- JournalInstance (MapID) -> JournalEncounter -> JournalEncounterCreature
-- (CreatureDisplayInfoID) -> creature_template.modelid1..4
-- Une seule creature par rencontre (celle qui la conclut), et uniquement
-- celles qui n'ont aucune table de butin, pour ne rien doubler.
--
-- Applique le 2026-09-10.
-- =====================================================================
DELETE FROM `creature_template_journal` WHERE `entry` IN (
74366,81297,80816,83612,87420,76865,76814,76906,76806,76974,76877,77692,77182,77557,
75964,76141,76143,82682,84550,83846,76413,76021,77120,76585,91005,94960,95674,96759,
102682,104154,106643,116407,115767,116691,118462,117269,118518,122316,122313,122056,
124729,98965,79852,79912);
INSERT INTO `creature_template_journal` (`entry`,`JournalEncounterID`) VALUES
-- Mines de Sangyre (1175)
(74366, 893), -- Forgemaster Gog'duh
-- Docks de fer (1195)
(81297, 1235), -- Dreadfang (Fleshrender Nok'gar)
(80816, 1236), -- Ahri'ok Dugru (Contremaitres du Rail-de-fer)
(79852, 1237), -- Oshir
(83612, 1238), -- Skulloc
-- Auchindoun (1182) : PAS de ligne pour Azzakel. Le vrai boss (75927,
-- niveau 97, script boss_azzakel, 49 lignes de butin) n'est PAS apparu ;
-- le PNJ pose dans le donjon est un doublon de niveau 1 sans script.
-- C'est un defaut d'apparition, pas de butin : a corriger separement.
-- Aire de Haut-perchoir (1209)
(75964, 965), -- Ranjit
(76141, 966), -- Araknath
(76143, 967), -- Rukhran
-- Floraison eternelle (1279)
(82682, 1208), -- Archmage Sol
(84550, 1209), -- Xeri'tac
(83846, 1210), -- Yalnu
-- Pic Rochenoire, sommet (1358)
(76413, 1226), -- Orebender Gor'ashan
(76021, 1227), -- Kyrak
(79912, 1228), -- Commander Tharbek
(76585, 1229), -- Ragewing the Untamed
(77120, 1234), -- Warlord Zaela
-- Fonderie des Rochenoire (1205) - raid entier sans butin
(77182, 1202), -- Oregorger
(76974, 1155), -- Franzok (Hans'gar et Franzok)
(76865, 1122), -- Beastlord Darmac
(76877, 1161), -- Gruul
(76814, 1123), -- Flamebender Ka'graz
(76906, 1147), -- Operator Thogar
(76806, 1154), -- Heart of the Mountain (Haut fourneau)
(77692, 1162), -- Kromog
(77557, 1203), -- Admiral Gar'an (Vierges de fer)
(87420, 959), -- Blackhand
-- Repaire de Neltharion (1458)
(91005, 1673), -- Naraxas
-- Salles des Valeureux (1477)
(94960, 1485), -- Hymdall
(95674, 1487), -- Fenryr
-- Gouffre des ames (1492)
(96759, 1663), -- Helya
-- Donjon de Tenebreterre (1501)
(98965, 1672), -- Kur'talos Ravencrest
-- Cauchemar d'emeraude (1520)
(102682, 1704), -- Lethon (Dragons du Cauchemar)
-- Nighthold (1530)
(104154, 1737), -- Gul'dan
(106643, 1743), -- Grande magistrix Elisande
-- Tombeau de Sargeras (1676)
(116407, 1856), -- Harjatan
(115767, 1861), -- Mistress Sassz'ine
(116691, 1867), -- Belac (Inquisition demoniaque)
(118518, 1903), -- Priestess Lunaspyre (Soeurs de la Lune)
(118462, 1896), -- Soul Queen Dejahna (Hote desole)
(117269, 1898), -- Kil'jaeden
-- Siege du Triumvirat (1753) - donjon entier sans butin
(122313, 1979), -- Zuraal the Ascended
(122316, 1980), -- Saprish
(122056, 1981), -- Viceroy Nezhar
(124729, 1982); -- L'ura
@@ -0,0 +1,6 @@
-- Rollback de butin_journal_wod_legion.sql (2026-09-10)
DELETE FROM `creature_template_journal` WHERE `entry` IN (
74366,81297,80816,83612,87420,76865,76814,76906,76806,76974,76877,77692,77182,77557,
75964,76141,76143,82682,84550,83846,76413,76021,77120,76585,91005,94960,95674,96759,
102682,104154,106643,116407,115767,116691,118462,117269,118518,122316,122313,122056,
124729,98965,79852,79912);
+42
View File
@@ -0,0 +1,42 @@
-- =====================================================================
-- Options de dialogue mortes qui doublonnent une option fonctionnelle
--
-- Une ligne de type 1 (bavardage) est affichee sans aucune verification
-- par Player::PrepareGossipMenu (« no checks »), mais sa selection ne
-- declenche aucune action. Lorsqu'une telle ligne porte le MEME texte
-- qu'une option reellement fonctionnelle du meme menu -- aubergiste (8),
-- marchand (3), transmogrification (16) -- le joueur voit la ligne en
-- double et celle du haut ne fait rien.
--
-- PREMIERE VERSION ERRONEE, CORRIGEE ICI
-- La version precedente supprimait aussi huit options portant des
-- CONDITIONS : evenement 12 (Halloween) actif et absence de l'aura
-- 24755 « Tricked or Treated ». C'etait l'option « Des bonbons ou un
-- sort ! », que la base habille du meme texte que l'option d'auberge.
-- Hors periode d'Halloween elle n'est jamais affichee : la supprimer
-- ne corrigeait rien et retirait du contenu saisonnier.
--
-- D'ou la clause NOT EXISTS : toute option soumise a une condition est
-- desormais preservee, sans avoir a juger de la condition elle-meme.
--
-- CE CORRECTIF NE RESOUT PAS le probleme de liaison de pierre de foyer
-- signale en jeu : le menu 342 fait partie des huit cas conditionnes,
-- son option morte n'etait donc pas affichee. La cause reste a trouver.
-- Il est conserve parce qu'il corrige un vrai defaut d'affichage
-- ailleurs, pas parce qu'il repond au symptome signale.
--
-- Retour arriere : chantiers/gossip_menu_option_retour_20260831.sql
-- =====================================================================
DELETE g1
FROM `gossip_menu_option` g1
JOIN `gossip_menu_option` g2
ON g2.`MenuId` = g1.`MenuId`
AND g2.`OptionText` = g1.`OptionText`
AND g2.`OptionType` IN (3, 8, 16)
WHERE g1.`OptionType` = 1
AND NOT EXISTS (SELECT 1
FROM `conditions` c
WHERE c.`SourceTypeOrReferenceId` = 15
AND c.`SourceGroup` = g1.`MenuId`
AND c.`SourceEntry` = g1.`OptionIndex`);
+37
View File
@@ -0,0 +1,37 @@
-- =====================================================================
-- Salles des Valeureux (carte 1477) : les 3 boss absents
--
-- Constat du 2026-09-10 : Hymdall, Fenryr et les quatre Rois valarjar
-- sont bien posés, mais Hyrja, le Roi-dieu Skovald et Odyn n'existent
-- nulle part dans `creature` — donc le donjon s'arrête après Fenryr.
-- Leurs modèles, factions, drapeaux, scripts et tables de butin sont
-- pourtant tous en base : il ne manquait que les apparitions.
--
-- Positions : Odyn est repris tel quel du script (boss_odyn.cpp,
-- SetHomePosition 2402.76 / 528.64 / 748.99) ; Skovald est placé dans le
-- hall d'Odyn, à l'intérieur de la porte 246145 (2520.3 / 529.0 / 749.0)
-- dont il commande la fermeture ; Hyrja et ses deux acolytes sont posés
-- au-delà de la porte de sa salle (245702, 3196.2 / 371.3 / 655.4), sur
-- l'axe d'entrée. La position de Hyrja est la seule estimée : si elle
-- flotte ou s'enfonce, corriger le seul Z avec un UPDATE.
--
-- Odyn a `lootid = 0` : sa ligne de Guide d'aventure est ajoutée ici
-- (rencontre 1489, 17 objets officiels). Hyrja et Skovald ont déjà leurs
-- tables de butin, on n'y touche pas.
-- =====================================================================
DELETE FROM `creature` WHERE `guid` BETWEEN 290200801 AND 290200805;
INSERT INTO `creature`
(`guid`,`id`,`map`,`zoneId`,`areaId`,`spawnDifficulties`,`phaseUseFlags`,`PhaseId`,`PhaseGroup`,
`terrainSwapMap`,`modelid`,`equipment_id`,`position_x`,`position_y`,`position_z`,`orientation`,
`spawntimesecs`,`spawndist`,`currentwaypoint`,`curhealth`,`curmana`,`MovementType`,
`npcflag`,`unit_flags`,`unit_flags2`,`unit_flags3`,`dynamicflags`,`ScriptName`,`movementmode`,`VerifiedBuild`) VALUES
(290200801, 95833, 1477, 7672, 7672, '1,2,8,23', 0,0,0, -1, 0,0, 3175.00, 350.00, 655.40, 0.79, 7200,0,0,0,0,0, 0,0,0,0,0, '', 0, 0), -- Hyrja
(290200802, 97202, 1477, 7672, 7672, '1,2,8,23', 0,0,0, -1, 0,0, 3166.50, 358.50, 655.40, 0.79, 7200,0,0,0,0,0, 0,0,0,0,0, '', 0, 0), -- Olmyr the Enlightened
(290200803, 97219, 1477, 7672, 7672, '1,2,8,23', 0,0,0, -1, 0,0, 3183.50, 341.50, 655.40, 0.79, 7200,0,0,0,0,0, 0,0,0,0,0, '', 0, 0), -- Solsten
(290200804, 95675, 1477, 7672, 7672, '1,2,8,23', 0,0,0, -1, 0,0, 2500.00, 529.00, 749.00, 0.00, 7200,0,0,0,0,0, 0,0,0,0,0, '', 0, 0), -- God-King Skovald
(290200805, 95676, 1477, 7672, 7672, '1,2,8,23', 0,0,0, -1, 0,0, 2402.76, 528.64, 748.99, 0.00, 7200,0,0,0,0,0, 0,0,0,0,0, '', 0, 0); -- Odyn
-- Odyn n'a aucune table de butin : on le branche sur le Guide d'aventure
DELETE FROM `creature_template_journal` WHERE `entry` = 95676;
INSERT INTO `creature_template_journal` (`entry`,`JournalEncounterID`) VALUES (95676, 1489);
@@ -0,0 +1,3 @@
-- Rollback de hov_boss_manquants.sql (2026-09-10)
DELETE FROM `creature` WHERE `guid` BETWEEN 290200801 AND 290200805;
DELETE FROM `creature_template_journal` WHERE `entry` = 95676;
+18
View File
@@ -0,0 +1,18 @@
-- =====================================================================
-- Rivage brisé — Krosus était inattaquable
--
-- SIGNALÉ EN JEU : « le Krosus du lac de lave en p8 est inattaquable ».
--
-- Il portait la faction 2878. C'est exactement le défaut résolu en août
-- pour les autres démons de cette carte : les factions 2780 et 1768 les
-- rendaient inattaquables — le refus vient du client, aucune sonde
-- serveur ne peut l'observer — et on les avait toutes basculées en 16,
-- la seule dont le fonctionnement soit démontré ici. La 2878 n'était
-- pas dans le lot, personne n'étant jamais allé aussi loin dans le
-- scénario.
--
-- PORTÉE VÉRIFIÉE : l'entrée 90544 n'existe QUE sur la carte 1460, un
-- seul exemplaire. Aucun autre contenu n'est touché.
-- =====================================================================
UPDATE `creature_template` SET `faction` = 16 WHERE `entry` = 90544;
+36
View File
@@ -0,0 +1,36 @@
-- =====================================================================
-- Rivage brisé — la mort de Tirion, mise en scène
--
-- SIGNALÉ EN JEU : « p7 c'est Krosus qui plonge Tirion dans le fiel
-- normalement, et là pas de script de scénario ».
--
-- Séquence officielle, relevée sur Warcraft Wiki :
-- Tirion « Stay back... it's a trap... the Light will protect me... »
-- Gul'dan « Ha, you fool! You stand before the temple of a GOD... »
-- Krosus surgit de la lave.
-- Gul'dan « Destroy him. »
-- Krosus souffle sur Tirion.
-- Tirion « The Light... will... ahh! Aahhhh!! » puis il sombre.
-- Thrall « Fordring! »
-- Gul'dan « All you have worked for... » puis « Destroy them! »
--
-- Les huit répliques portent leur BroadcastTextId et leur Sound
-- d'origine : le client jouera la voix française et affichera son
-- propre texte. Le texte anglais porté ici n'est qu'un repère.
--
-- Groupes 20 et au-delà, pour ne rien écraser.
-- =====================================================================
DELETE FROM `creature_text` WHERE `GroupID` BETWEEN 20 AND 29
AND `CreatureID` IN (90367, 90413, 90711, 90713);
INSERT INTO `creature_text`
(`CreatureID`,`GroupID`,`ID`,`Text`,`Type`,`Language`,`Probability`,`Emote`,`Duration`,`Sound`,`BroadcastTextId`,`TextRange`,`comment`) VALUES
(90367,20,0,'Stay back... it''s a trap... the Light will protect me...',14,0,100,0,0,53452,99237,0,'mort de Tirion 1'),
(90367,21,0,'The Light... will... ahh! Aahhhh!!',14,0,100,0,0,53455,99240,0,'mort de Tirion 5'),
(90413,20,0,'Ha, you fool! You stand before the temple of a GOD. Your pitiful Light cannot reach you here.',14,0,100,0,0,53453,99238,0,'mort de Tirion 2'),
(90413,21,0,'Destroy him.',14,0,100,0,0,53454,99239,0,'mort de Tirion 4'),
(90413,22,0,'All you have worked for, all you have sacrificed, just to see your champions fall to ash one by one.',14,0,100,0,0,53458,99243,0,'mort de Tirion 7'),
(90413,23,0,'Destroy them!',14,0,100,0,0,53462,99247,0,'mort de Tirion 8'),
(90711,20,0,'Fordring!',14,0,100,0,0,53456,99241,0,'mort de Tirion 6'),
(90713,20,0,'We will never give in to hopelessness!',14,0,100,0,0,53459,99244,0,'mort de Tirion, riposte');
+34
View File
@@ -0,0 +1,34 @@
-- =====================================================================
-- Rivage brisé (carte 1460) — objets affichés comme déjà utilisés
--
-- SIGNALÉ EN JEU : « le gameobject de la flèche n'est physiquement pas
-- visible ni cliquable, à la place il y a un minuscule skin de chien ».
--
-- Les six Flèches de malheur (240194) étaient enregistrées en
-- `state = 2`, soit GO_STATE_ACTIVE_ALTERNATIVE. Le commentaire de notre
-- propre énumération le décrit sans ambiguïté :
-- « show in world as used in alt way and not reset »
-- L'objet est donc affiché dans sa forme CONSOMMÉE dès l'apparition,
-- avec le modèle de remplacement correspondant — et il n'est plus
-- utilisable, puisqu'il se présente comme déjà employé.
--
-- Ce n'était ni le modèle ni l'orientation : `displayId` 37460 existe
-- bien dans GameObjectDisplayInfo du build 7.3.5.26972, avec une boîte
-- englobante de 44 × 44 × 71 unités, et la rotation (0,0,0,1) est
-- l'identité, la même que celle des Cages de la Légion qui s'affichent
-- correctement.
--
-- État attendu : GO_STATE_READY (1), celui de 79 des 101 objets de la
-- carte, dont toutes les cages, les fleurs et les navires.
--
-- PORTÉE : les six flèches uniquement. Les Canons de siège (240214) et
-- deux Portails de la Légion partagent le même défaut apparent, mais
-- rien ne prouve encore que leur état soit fautif — un canon peut
-- légitimement s'afficher chargé. À vérifier en jeu avant d'y toucher.
-- =====================================================================
UPDATE `gameobject`
SET `state` = 1
WHERE `id` = 240194
AND `map` = 1460
AND `state` = 2;
+65
View File
@@ -0,0 +1,65 @@
-- =====================================================================
-- Rivage brisé (1460) — la parole et la VOIX rendues aux personnages
--
-- SIGNALÉ EN JEU : « tous les PNJ de la zone sont totalement
-- inexpressifs, ça n'a pas d'âme », « tout est muet même s'il y a du
-- textuel ».
--
-- CAUSE : nos répliques portent un BroadcastTextId à ZÉRO. Le serveur
-- envoie alors du texte brut, que le client affiche sans rien avoir à
-- résoudre — donc sans la version localisée et SANS LA BANDE SON. Le
-- client possède pourtant les deux : c'est pour cela que la vidéo
-- montre du français alors que notre base n'en contient aucune
-- traduction. Il ne lui manquait que l'identifiant.
--
-- SOURCE : creature_text du dump dufernst/LegionCore-7.3.5, qui a
-- conservé les Sound et BroadcastTextID d'origine. Son texte est en
-- russe, sans importance : l'identifiant prime, et le client rendra sa
-- propre version. Le texte porté ici est l'anglais officiel, tiré de
-- notre broadcast_text — il ne sert que de repli et de repère.
--
-- GROUPES DÉCALÉS DE +10. Nos répliques existantes, aux groupes 0 et 1,
-- sont CONSERVÉES : le script les appelle par Talk(0) et Talk(1), et
-- les écraser ferait dire à Varian « Pour l'Alliance ! » au moment de
-- mourir. Le recâblage du script est un travail distinct.
--
-- 34 répliques, toutes avec son et identifiant de diffusion.
-- =====================================================================
DELETE FROM `creature_text` WHERE `GroupID` >= 10 AND `CreatureID` IN (90544,90708,90709,90710,90711,90713,90714,90716,90717);
INSERT INTO `creature_text` (`CreatureID`,`GroupID`,`ID`,`Text`,`Type`,`Language`,`Probability`,`Emote`,`Duration`,`Sound`,`BroadcastTextId`,`TextRange`,`comment`) VALUES
(90544,10,0,'You will know fear.',14,0,100,0,0,53463,99248,0,'voix officielle -- son 53463'),
(90708,10,0,'Listen up! Dere''s only one way offa dis beach, and it be t''rough alla dose demons ahead of us. But dey don''t know who dey be dealin'' wit. Let''s show dem what it means ta be Horde!',14,0,100,0,0,53528,99315,0,'voix officielle -- son 53528'),
(90708,11,0,'Die.',14,0,100,0,0,53536,99323,0,'voix officielle -- son 53536'),
(90708,13,0,'',14,0,100,0,0,53590,143151,0,'voix officielle -- son 53590'),
(90708,15,0,'Dis one''s mine.',14,0,100,0,0,53537,99324,0,'voix officielle -- son 53537'),
(90709,10,0,'',14,0,100,0,0,53590,99375,0,'voix officielle -- son 53590'),
(90709,11,0,'',14,0,100,0,0,53587,99374,0,'voix officielle -- son 53587'),
(90709,12,0,'',14,0,100,0,0,53465,99250,0,'voix officielle -- son 53465'),
(90709,13,0,'',14,0,100,0,0,53585,99372,0,'voix officielle -- son 53585'),
(90709,14,0,'',12,0,100,0,0,53610,99397,0,'voix officielle -- son 53610'),
(90709,15,0,'',12,0,100,0,0,53612,99399,0,'voix officielle -- son 53612'),
(90710,14,0,'By the Earthmother!',14,0,100,0,0,53591,109421,0,'voix officielle -- son 53591'),
(90711,10,0,'The elements will destroy you!',14,0,100,0,0,53534,99321,0,'voix officielle -- son 53534'),
(90711,11,0,'Back to the Nether!',14,0,100,0,0,53533,99320,0,'voix officielle -- son 53533'),
(90711,12,0,'Spirits of earth, aid me!',12,0,100,0,0,53611,99398,0,'voix officielle -- son 53611'),
(90713,10,0,'Brace yourselves, Alliance, it''s coming this way!',14,0,100,0,0,53464,99249,0,'voix officielle -- son 53464'),
(90713,11,0,'',14,0,100,0,0,53407,146212,0,'voix officielle -- son 53407'),
(90713,14,0,'We''re pushing them back! Don''t let up!',14,0,100,0,0,53405,99189,0,'voix officielle -- son 53405'),
(90713,15,0,'There is nowhere to run, Gul''dan. Give up now, and I will grant you a swift death.',14,0,100,0,0,53496,99283,0,'voix officielle -- son 53496'),
(90713,16,0,'Hold steady, we''ve broken their lines before, we will again. Charge!',14,0,100,0,0,53498,99285,0,'voix officielle -- son 53498'),
(90713,17,0,'No matter how many demons you throw at us, we will cut you down, monster.',14,0,100,0,0,53513,99300,0,'voix officielle -- son 53513'),
(90714,10,0,'',14,0,100,0,0,53345,94240,0,'voix officielle -- son 53345'),
(90714,11,0,'',14,0,100,0,0,53347,94242,0,'voix officielle -- son 53347'),
(90714,12,0,'',14,0,100,0,0,53357,94594,0,'voix officielle -- son 53357'),
(90714,13,0,'',12,0,100,0,0,53449,99234,0,'voix officielle -- son 53449'),
(90714,14,0,'',14,0,100,0,0,53358,94595,0,'voix officielle -- son 53358'),
(90716,10,0,'How are we going to get across?',12,0,100,1,0,53448,99233,0,'voix officielle -- son 53448'),
(90716,11,0,'For Gnomeregan!',14,0,100,0,0,53409,99193,0,'voix officielle -- son 53409'),
(90716,12,0,'',14,0,100,0,0,53408,127434,0,'voix officielle -- son 53408'),
(90717,10,0,'Just in time. We haven''t been able to break their line, now we may have a chance.',14,0,100,0,0,53346,94241,0,'voix officielle -- son 53346'),
(90717,11,0,'There''s only one way out of this, Alliance, and it''s through that line of demons! Cannons, lay down covering fire! All forces, CHARGE!',14,0,100,0,0,53348,94585,0,'voix officielle -- son 53348'),
(90717,12,0,'Go for the throat!',14,0,100,0,0,53354,94591,0,'voix officielle -- son 53354'),
(90717,13,0,'This one''s mine!',14,0,100,0,0,53355,117408,0,'voix officielle -- son 53355'),
(90717,14,0,'Side by side, don''t let them break through!',14,0,100,0,0,53353,94590,0,'voix officielle -- son 53353');
+98
View File
@@ -0,0 +1,98 @@
/*M!999999\- enable the sandbox mode */
-- MariaDB dump 10.19-11.4.7-MariaDB, for debian-linux-gnu (x86_64)
--
-- Host: localhost Database: dc_world
-- ------------------------------------------------------
-- Server version 11.4.7-MariaDB-0ubuntu0.25.04.1
/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
/*!40101 SET NAMES utf8mb4 */;
/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */;
/*!40103 SET TIME_ZONE='+00:00' */;
/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
/*M!100616 SET @OLD_NOTE_VERBOSITY=@@NOTE_VERBOSITY, NOTE_VERBOSITY=0 */;
--
-- Dumping data for table `creature_text`
--
-- WHERE: CreatureID IN (90367,90413,90544,90705,90708,90709,90710,90711,90713,90714,90716,90717,91951,94276)
LOCK TABLES `creature_text` WRITE;
/*!40000 ALTER TABLE `creature_text` DISABLE KEYS */;
INSERT INTO `creature_text` VALUES (90367,0,0,'Ne vous souciez pas de moi ! Le tombeau... Gul\'dan ouvre le tombeau ! Arrêtez-le !',14,0,100,5,0,0,0,0,'Tirion crevasse');
INSERT INTO `creature_text` VALUES (90367,20,0,'Stay back... it\'s a trap... the Light will protect me...',14,0,100,0,0,53452,99237,0,'mort de Tirion 1');
INSERT INTO `creature_text` VALUES (90367,21,0,'The Light... will... ahh! Aahhhh!!',14,0,100,0,0,53455,99240,0,'mort de Tirion 5');
INSERT INTO `creature_text` VALUES (90413,0,0,'Vous arrivez trop tard ! Les ténèbres s\'éveillent sous ce tombeau !',14,0,100,25,0,0,0,0,'Guldan accueil');
INSERT INTO `creature_text` VALUES (90413,1,0,'Sargeras... accueille leurs âmes !',14,0,100,25,0,0,0,0,'Guldan invocation');
INSERT INTO `creature_text` VALUES (90413,2,0,'Cette victoire ne changera rien. L\'Étoile ardente approche !',14,0,100,25,0,0,0,0,'Guldan retraite');
INSERT INTO `creature_text` VALUES (90413,20,0,'Ha, you fool! You stand before the temple of a GOD. Your pitiful Light cannot reach you here.',14,0,100,0,0,53453,99238,0,'mort de Tirion 2');
INSERT INTO `creature_text` VALUES (90413,21,0,'Destroy him.',14,0,100,0,0,53454,99239,0,'mort de Tirion 4');
INSERT INTO `creature_text` VALUES (90413,22,0,'All you have worked for, all you have sacrificed, just to see your champions fall to ash one by one.',14,0,100,0,0,53458,99243,0,'mort de Tirion 7');
INSERT INTO `creature_text` VALUES (90413,23,0,'Destroy them!',14,0,100,0,0,53462,99247,0,'mort de Tirion 8');
INSERT INTO `creature_text` VALUES (90544,10,0,'You will know fear.',14,0,100,0,0,53463,99248,0,'voix officielle -- son 53463');
INSERT INTO `creature_text` VALUES (90705,0,0,'Vos armées ne sont que poussière. Je suis Arganoth, et cette plage sera votre tombeau !',14,0,100,15,0,0,0,0,'Arganoth spawn');
INSERT INTO `creature_text` VALUES (90705,1,0,'La Légion... est... éternelle...',14,0,100,0,0,0,0,0,'Arganoth mort');
INSERT INTO `creature_text` VALUES (90708,0,0,'Fils et filles de la Horde ! Montrez à la Légion c\'que vaut not\' sang ! LOK\'TAR OGAR !',14,0,100,5,0,53528,99315,0,'Voljin ralliement');
INSERT INTO `creature_text` VALUES (90708,1,0,'Argh... la lame... empoisonnée... Sylvanas... sonne la retraite...',14,0,100,0,0,0,0,0,'Voljin blessé');
INSERT INTO `creature_text` VALUES (90708,10,0,'Listen up! Dere\'s only one way offa dis beach, and it be t\'rough alla dose demons ahead of us. But dey don\'t know who dey be dealin\' wit. Let\'s show dem what it means ta be Horde!',14,0,100,0,0,53528,99315,0,'voix officielle -- son 53528');
INSERT INTO `creature_text` VALUES (90708,11,0,'Die.',14,0,100,0,0,53536,99323,0,'voix officielle -- son 53536');
INSERT INTO `creature_text` VALUES (90708,13,0,'',14,0,100,0,0,53590,143151,0,'voix officielle -- son 53590');
INSERT INTO `creature_text` VALUES (90708,15,0,'Dis one\'s mine.',14,0,100,0,0,53537,99324,0,'voix officielle -- son 53537');
INSERT INTO `creature_text` VALUES (90709,0,0,'La Horde ne mourra pas sur cette plage. Avancez, et que la Légion tombe !',14,0,100,25,0,53587,99374,0,'Sylvanas');
INSERT INTO `creature_text` VALUES (90709,10,0,'',14,0,100,0,0,53590,99375,0,'voix officielle -- son 53590');
INSERT INTO `creature_text` VALUES (90709,11,0,'',14,0,100,0,0,53587,99374,0,'voix officielle -- son 53587');
INSERT INTO `creature_text` VALUES (90709,12,0,'',14,0,100,0,0,53465,99250,0,'voix officielle -- son 53465');
INSERT INTO `creature_text` VALUES (90709,13,0,'',14,0,100,0,0,53585,99372,0,'voix officielle -- son 53585');
INSERT INTO `creature_text` VALUES (90709,14,0,'',12,0,100,0,0,53610,99397,0,'voix officielle -- son 53610');
INSERT INTO `creature_text` VALUES (90709,15,0,'',12,0,100,0,0,53612,99399,0,'voix officielle -- son 53612');
INSERT INTO `creature_text` VALUES (90710,14,0,'By the Earthmother!',14,0,100,0,0,53591,109421,0,'voix officielle -- son 53591');
INSERT INTO `creature_text` VALUES (90711,10,0,'The elements will destroy you!',14,0,100,0,0,53534,99321,0,'voix officielle -- son 53534');
INSERT INTO `creature_text` VALUES (90711,11,0,'Back to the Nether!',14,0,100,0,0,53533,99320,0,'voix officielle -- son 53533');
INSERT INTO `creature_text` VALUES (90711,12,0,'Spirits of earth, aid me!',12,0,100,0,0,53611,99398,0,'voix officielle -- son 53611');
INSERT INTO `creature_text` VALUES (90711,20,0,'Fordring!',14,0,100,0,0,53456,99241,0,'mort de Tirion 6');
INSERT INTO `creature_text` VALUES (90713,0,0,'Soldats de l\'Alliance ! La Légion s\'abat sur nos terres. Repoussez-la jusqu\'aux portes du tombeau de Sargeras !',14,0,100,5,0,53498,99285,0,'Varian ralliement');
INSERT INTO `creature_text` VALUES (90713,1,0,'Partez, sauvez ce qui peut l\'être ! Dites à mon fils... que je suis mort en roi. Pour l\'Alliance !',14,0,100,5,0,53407,146212,0,'Varian sacrifice');
INSERT INTO `creature_text` VALUES (90713,10,0,'Brace yourselves, Alliance, it\'s coming this way!',14,0,100,0,0,53464,99249,0,'voix officielle -- son 53464');
INSERT INTO `creature_text` VALUES (90713,11,0,'',14,0,100,0,0,53407,146212,0,'voix officielle -- son 53407');
INSERT INTO `creature_text` VALUES (90713,14,0,'We\'re pushing them back! Don\'t let up!',14,0,100,0,0,53405,99189,0,'voix officielle -- son 53405');
INSERT INTO `creature_text` VALUES (90713,15,0,'There is nowhere to run, Gul\'dan. Give up now, and I will grant you a swift death.',14,0,100,0,0,53496,99283,0,'voix officielle -- son 53496');
INSERT INTO `creature_text` VALUES (90713,16,0,'Hold steady, we\'ve broken their lines before, we will again. Charge!',14,0,100,0,0,53498,99285,0,'voix officielle -- son 53498');
INSERT INTO `creature_text` VALUES (90713,17,0,'No matter how many demons you throw at us, we will cut you down, monster.',14,0,100,0,0,53513,99300,0,'voix officielle -- son 53513');
INSERT INTO `creature_text` VALUES (90713,20,0,'We will never give in to hopelessness!',14,0,100,0,0,53459,99244,0,'mort de Tirion, riposte');
INSERT INTO `creature_text` VALUES (90714,0,0,'Leurs portails déversent des renforts sans fin ! Détruisez les ancres dimensionnelles !',14,0,100,5,0,53345,94240,0,'Jaina portails');
INSERT INTO `creature_text` VALUES (90714,10,0,'',14,0,100,0,0,53345,94240,0,'voix officielle -- son 53345');
INSERT INTO `creature_text` VALUES (90714,11,0,'',14,0,100,0,0,53347,94242,0,'voix officielle -- son 53347');
INSERT INTO `creature_text` VALUES (90714,12,0,'',14,0,100,0,0,53357,94594,0,'voix officielle -- son 53357');
INSERT INTO `creature_text` VALUES (90714,13,0,'',12,0,100,0,0,53449,99234,0,'voix officielle -- son 53449');
INSERT INTO `creature_text` VALUES (90714,14,0,'',14,0,100,0,0,53358,94595,0,'voix officielle -- son 53358');
INSERT INTO `creature_text` VALUES (90716,10,0,'How are we going to get across?',12,0,100,1,0,53448,99233,0,'voix officielle -- son 53448');
INSERT INTO `creature_text` VALUES (90716,11,0,'For Gnomeregan!',14,0,100,0,0,53409,99193,0,'voix officielle -- son 53409');
INSERT INTO `creature_text` VALUES (90716,12,0,'',14,0,100,0,0,53408,127434,0,'voix officielle -- son 53408');
INSERT INTO `creature_text` VALUES (90717,10,0,'Just in time. We haven\'t been able to break their line, now we may have a chance.',14,0,100,0,0,53346,94241,0,'voix officielle -- son 53346');
INSERT INTO `creature_text` VALUES (90717,11,0,'There\'s only one way out of this, Alliance, and it\'s through that line of demons! Cannons, lay down covering fire! All forces, CHARGE!',14,0,100,0,0,53348,94585,0,'voix officielle -- son 53348');
INSERT INTO `creature_text` VALUES (90717,12,0,'Go for the throat!',14,0,100,0,0,53354,94591,0,'voix officielle -- son 53354');
INSERT INTO `creature_text` VALUES (90717,13,0,'This one\'s mine!',14,0,100,0,0,53355,117408,0,'voix officielle -- son 53355');
INSERT INTO `creature_text` VALUES (90717,14,0,'Side by side, don\'t let them break through!',14,0,100,0,0,53353,94590,0,'voix officielle -- son 53353');
INSERT INTO `creature_text` VALUES (91951,20,0,'Stay back... it\'s a trap... the Light will protect me...',14,0,100,0,0,53452,99237,0,'mort de Tirion 1');
INSERT INTO `creature_text` VALUES (91951,21,0,'The Light... will... ahh! Aahhhh!!',14,0,100,0,0,53455,99240,0,'mort de Tirion 5');
INSERT INTO `creature_text` VALUES (94276,0,0,'I have seen the end of your pitiful world, Wrynn. You will perish in felfire.',14,0,100,0,0,53506,99293,0,'finale Guldan 1');
INSERT INTO `creature_text` VALUES (94276,2,0,'Destroy them!',14,0,100,0,0,53462,99247,0,'finale Guldan 2');
INSERT INTO `creature_text` VALUES (94276,20,0,'Ha, you fool! You stand before the temple of a GOD. Your pitiful Light cannot reach you here.',14,0,100,0,0,53453,99238,0,'mort de Tirion 2');
INSERT INTO `creature_text` VALUES (94276,21,0,'Destroy him.',14,0,100,0,0,53454,99239,0,'mort de Tirion 4');
INSERT INTO `creature_text` VALUES (94276,22,0,'All you have worked for, all you have sacrificed, just to see your champions fall to ash one by one.',14,0,100,0,0,53458,99243,0,'mort de Tirion 7');
INSERT INTO `creature_text` VALUES (94276,23,0,'Destroy them!',14,0,100,0,0,53462,99247,0,'mort de Tirion 8');
/*!40000 ALTER TABLE `creature_text` ENABLE KEYS */;
UNLOCK TABLES;
/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */;
/*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;
/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;
/*M!100616 SET NOTE_VERBOSITY=@OLD_NOTE_VERBOSITY */;
-- Dump completed on 2026-09-05 0:06:36
+114
View File
@@ -0,0 +1,114 @@
-- =====================================================================
-- Assaut du Rivage brisé (carte 1666) — les 5 chemins scriptés
--
-- Ces chemins vivent dans `waypoint_data_script` chez la référence,
-- une table que notre cœur n'a pas : sWaypointMgr ne lit que
-- `waypoint_data`. L'extraction du 27/08 ne regardait que cette
-- dernière et les avait donc manqués — d'où des alliés immobiles et,
-- plus grave, un corbeau arcanique qui ne décollait jamais.
--
-- Le 11322708 est le vol d'arrivée : c'est l'atteinte de son point 20
-- qui déclenche l'étape 0 du scénario. Sans lui, rien ne démarre.
--
-- move_flag → move_type : la valeur est 1 sur les 87 points.
-- Notre énumération donne 0=marche, 1=course, 2=atterrissage,
-- 3=décollage ; la course correspond. `speed` et `entry` n'ont
-- aucun équivalent chez nous et sont abandonnées.
--
-- 11322705 : 24 points
-- 11322706 : 18 points
-- 11322707 : 23 points
-- 11322708 : 21 points
-- 11322709 : 1 points
-- =====================================================================
DELETE FROM `waypoint_data` WHERE `id` BETWEEN 11322705 AND 11322709;
INSERT INTO `waypoint_data` (`id`,`point`,`position_x`,`position_y`,`position_z`,`orientation`,`delay`,`move_type`,`action`,`action_chance`,`wpguid`) VALUES
(11322705,1,-1895.48,3181.44,3.94356,0,0,1,0,100,0),
(11322705,2,-1895.48,3181.44,3.94356,0,0,1,0,100,0),
(11322705,3,-1896.09,3194.66,3.0589,0,0,1,0,100,0),
(11322705,4,-1879.45,3226.71,5.81127,0,0,1,0,100,0),
(11322705,5,-1855.82,3233.47,14.6567,0,0,1,0,100,0),
(11322705,6,-1857.58,3265.25,25.8483,0,0,1,0,100,0),
(11322705,7,-1867.03,3288.59,32.05,0,0,1,0,100,0),
(11322705,8,-1852.08,3304.66,37.4348,0,0,1,0,100,0),
(11322705,9,-1839.8,3322.75,40.8837,0,0,1,0,100,0),
(11322705,10,-1839.04,3343.29,42.466,0,0,1,0,100,0),
(11322705,11,-1836.7,3363.89,45.3895,0,0,1,0,100,0),
(11322705,12,-1850.58,3384.72,46.7613,0,0,1,0,100,0),
(11322705,13,-1825.16,3371.11,48.1092,0,0,1,0,100,0),
(11322705,14,-1800.48,3368.29,52.986,0,0,1,0,100,0),
(11322705,15,-1781.35,3380.74,58.1253,0,0,1,0,100,0),
(11322705,16,-1767.17,3386.07,61.2137,0,0,1,0,100,0),
(11322705,17,-1749.35,3385.12,65.4982,0,0,1,0,100,0),
(11322705,18,-1743.22,3376.4,66.7185,0,0,1,0,100,0),
(11322705,19,-1733.85,3361.1,69.025,0,0,1,0,100,0),
(11322705,20,-1727.28,3348.62,73.3198,0,0,1,0,100,0),
(11322705,21,-1719.09,3337.02,76.6384,0,0,1,0,100,0),
(11322705,22,-1710.71,3331.34,79.6968,0,0,1,0,100,0),
(11322705,23,-1693.39,3326.75,83.6209,0,0,1,0,100,0),
(11322705,24,-1665.47,3322.98,84.213,0,0,1,0,100,0),
(11322706,1,-1661.46,3342.18,84.7206,0,0,1,0,100,0),
(11322706,2,-1661.46,3342.18,84.7206,0,0,1,0,100,0),
(11322706,3,-1643.34,3358.99,91.1839,0,0,1,0,100,0),
(11322706,4,-1614.3,3365.34,99.9272,0,0,1,0,100,0),
(11322706,5,-1590.46,3353.51,107.609,0,0,1,0,100,0),
(11322706,6,-1568.88,3331.64,108.463,0,0,1,0,100,0),
(11322706,7,-1544.19,3323.67,108.452,0,0,1,0,100,0),
(11322706,8,-1527.78,3312.48,108.452,0,0,1,0,100,0),
(11322706,9,-1516.94,3278.18,108.456,0,0,1,0,100,0),
(11322706,10,-1502.84,3232.22,108.308,0,0,1,0,100,0),
(11322706,11,-1499.05,3199.12,94.6383,0,0,1,0,100,0),
(11322706,12,-1506.8,3164.73,85.6659,0,0,1,0,100,0),
(11322706,13,-1512.61,3132.76,85.6753,0,0,1,0,100,0),
(11322706,14,-1504.42,3115.11,83.3742,0,0,1,0,100,0),
(11322706,15,-1491.75,3090.42,72.0224,0,0,1,0,100,0),
(11322706,16,-1472.63,3055.67,53.7606,0,0,1,0,100,0),
(11322706,17,-1463.55,3041.25,48.0012,0,0,1,0,100,0),
(11322706,18,-1443.16,3018.53,36.9585,0,0,1,0,100,0),
(11322707,1,-1426.19,2996.66,37.1142,0,0,1,0,100,0),
(11322707,2,-1426.19,2996.66,37.1142,0,0,1,0,100,0),
(11322707,3,-1420.86,2984.12,35.987,0,0,1,0,100,0),
(11322707,4,-1414.93,2950.44,36.6867,0,0,1,0,100,0),
(11322707,5,-1402.61,2927.92,31.2816,0,0,1,0,100,0),
(11322707,6,-1382.3,2939.2,25.4175,0,0,1,0,100,0),
(11322707,7,-1359.37,2925.01,23.9701,0,0,1,0,100,0),
(11322707,8,-1348.45,2910.15,24.3147,0,0,1,0,100,0),
(11322707,9,-1351.97,2894.62,24.8695,0,0,1,0,100,0),
(11322707,10,-1350.41,2876.29,24.9108,0,0,1,0,100,0),
(11322707,11,-1344.7,2850.11,25.1457,0,0,1,0,100,0),
(11322707,12,-1325.29,2832.26,24.2901,0,0,1,0,100,0),
(11322707,13,-1290.08,2823.92,26.1421,0,0,1,0,100,0),
(11322707,14,-1318.28,2814.4,23.9136,0,0,1,0,100,0),
(11322707,15,-1321.91,2792.99,23.499,0,0,1,0,100,0),
(11322707,16,-1316.96,2769.79,23.9586,0,0,1,0,100,0),
(11322707,17,-1316.57,2747.45,24.3456,0,0,1,0,100,0),
(11322707,18,-1333.14,2704.89,30.3677,0,0,1,0,100,0),
(11322707,19,-1256.09,2718.4,27.7485,0,0,1,0,100,0),
(11322707,20,-1276.42,2664.57,28.3643,0,0,1,0,100,0),
(11322707,21,-1291.27,2637.64,32.6723,0,0,1,0,100,0),
(11322707,22,-1227.07,2659.19,27.5531,0,0,1,0,100,0),
(11322707,23,-1199.42,2661.52,29.5922,0,0,1,0,100,0),
(11322708,1,-850.003,4176.37,751.63,0,0,1,0,100,0),
(11322708,2,-898.993,4094.4,725.061,0,0,1,0,100,0),
(11322708,3,-970.247,3975.18,686.417,0,0,1,0,100,0),
(11322708,4,-1044.75,3879.26,652.558,0,0,1,0,100,0),
(11322708,5,-1151.45,3694.79,592.72,0,0,1,0,100,0),
(11322708,6,-1195.84,3493.36,546.951,0,0,1,0,100,0),
(11322708,7,-1225.72,3286.17,564.777,0,0,1,0,100,0),
(11322708,8,-1194.84,3183.06,550.694,0,0,1,0,100,0),
(11322708,9,-1057.13,3080.14,501.312,0,0,1,0,100,0),
(11322708,10,-1007.26,2974.9,455.169,0,0,1,0,100,0),
(11322708,11,-1019.28,2874.84,399.954,0,0,1,0,100,0),
(11322708,12,-1100.81,2821.6,336.706,0,0,1,0,100,0),
(11322708,13,-1194.32,2756.01,271.367,0,0,1,0,100,0),
(11322708,14,-1242.8,2712.92,285.178,0,0,1,0,100,0),
(11322708,15,-1365.37,2573.14,261.881,0,0,1,0,100,0),
(11322708,16,-1418.23,2507.59,254.915,0,0,1,0,100,0),
(11322708,17,-1495.32,2527.83,191.514,0,0,1,0,100,0),
(11322708,18,-1589.36,2571.82,132.87,0,0,1,0,100,0),
(11322708,19,-1660.26,2642.93,75.1266,0,0,1,0,100,0),
(11322708,20,-1719.28,2723.04,41.4641,0,0,1,0,100,0),
(11322708,21,-1716.3,2809.05,4,0,0,1,0,100,0),
(11322709,1,-1338.13,2800.24,23.0393,0,0,1,0,100,0);
+30
View File
@@ -0,0 +1,30 @@
-- =====================================================================
-- Assaut du Rivage brisé — le donneur manquant de « Begin the Assault »
--
-- La quête 45102 n'avait NI donneur NI receveur en base : introuvable en
-- jeu, quoi qu'on fasse. C'était l'une des trois quêtes sans donneur
-- relevées à l'audit du 23/08.
--
-- Le dump de référence (dufernst/LegionCore-7.3.5) donne pour elle
-- l'archimage Khadgar 116302, à la fois donneur et receveur. Ce PNJ est
-- DÉJÀ posé chez nous, sur la carte 1220 à (-1628, 3194) — exactement
-- les coordonnées de la référence — et porte déjà le drapeau de donneur
-- de quêtes (npcflag 3). Rien à faire apparaître : seule la déclaration
-- manquait.
--
-- 46734 reçoit aussi 116302, comme dans la référence, en plus du 86563
-- déjà déclaré chez nous. Deux points d'accès, aucun conflit.
--
-- Rappel de la chaîne, pour mémoire :
-- 46730 « Armies of Legionfall » 86563 (déjà câblée)
-- 45102 « Begin the Assault » 116302 (posée ici)
-- 46734 « Assault on Broken Shore » 86563 + 116302
-- =====================================================================
DELETE FROM `creature_queststarter` WHERE `quest` = 45102 AND `id` = 116302;
DELETE FROM `creature_questender` WHERE `quest` = 45102 AND `id` = 116302;
DELETE FROM `creature_queststarter` WHERE `quest` = 46734 AND `id` = 116302;
DELETE FROM `creature_questender` WHERE `quest` = 46734 AND `id` = 116302;
INSERT INTO `creature_queststarter` (`id`,`quest`) VALUES (116302, 45102), (116302, 46734);
INSERT INTO `creature_questender` (`id`,`quest`) VALUES (116302, 45102), (116302, 46734);
+39
View File
@@ -0,0 +1,39 @@
-- =====================================================================
-- Assaut du Rivage brisé (carte 1666) — rattachement des scripts
--
-- Le C++ porté (scenario_assault_broken_shore.cpp) ne sert à rien tant
-- que ses scripts ne sont liés à aucune entrée : c'est la classe de bug
-- la plus fréquente de ce cœur, des IA complètes jamais appelées.
--
-- PORTÉE VÉRIFIÉE : les onze entrées ci-dessous n'existent QUE sur la
-- carte 1666 — contrôlé par un GROUP BY sur (id, map). Un ScriptName
-- s'appliquant à toutes les cartes, ce contrôle n'est pas facultatif.
--
-- NE SONT PAS RATTACHÉES, volontairement : Arganoth (118551),
-- Mephistroth (120746), Illidan (119130) et Lord Kalgorath (116291).
-- L'instance les pilote depuis OnCreatureCreate et OnCreatureKilled ;
-- leur donner un ScriptName ferait double emploi.
--
-- CONCORDANCE RELEVÉE, pas supposée :
-- 120743 Arcane Bomb → 7 exemplaires posés, critère 57574 × 7
-- 118558 Legion Portal → 7 exemplaires, dont 3 à fermer (critère 56778)
-- =====================================================================
-- Les huit alliés qui accompagnent l'assaut : rotations de sorts et
-- recherche d'adversaire à portée.
UPDATE `creature_template` SET `ScriptName` = 'npc_assaut_allie'
WHERE `entry` IN (119133, 118412, 121232, 118966, 121146, 118969, 118945, 118444);
-- Le corbeau arcanique : véhicule d'arrivée, sa fin de trajet vaut
-- l'étape 0 du scénario. Aucun spawn en base, il est invoqué par sort.
UPDATE `creature_template` SET `ScriptName` = 'npc_assaut_corbeau_arcanique'
WHERE `entry` = 118517;
-- Portails de la Légion et bombes arcaniques : activés au clic de sort.
UPDATE `creature_template` SET `ScriptName` = 'npc_assaut_interactif'
WHERE `entry` IN (118558, 120743);
-- La passerelle démoniaque, objet de type 10 (goober) : notre cœur ne
-- lui offre que le point d'entrée OnGossipHello.
UPDATE `gameobject_template` SET `ScriptName` = 'go_assaut_passerelle_demoniaque'
WHERE `entry` = 267955;
+53
View File
@@ -0,0 +1,53 @@
-- =====================================================================
-- Rivage brisé — armer les alliés
--
-- SIGNALÉ EN JEU : « les PNJ ne portent encore aucune arme ».
--
-- L'état de fourreau ne suffisait pas : il n'y avait rien à
-- dégainer. Sur les 51 entrées alliées de la carte, seules QUATRE
-- avaient un équipement défini — les 47 autres n'ont jamais eu
-- d'arme, ni en base ni sur leur modèle.
--
-- Source : creature_equip_template du dump de référence
-- dufernst/LegionCore-7.3.5, qui les renseigne. 30 équipements
-- pour autant d'entrées.
--
-- Les spawns de la carte qui n'en portaient aucun se voient
-- attribuer le jeu numéro 1 de leur entrée, quand il existe.
-- =====================================================================
INSERT INTO `creature_equip_template` (`CreatureID`,`ID`,`ItemID1`,`ItemID2`,`ItemID3`) VALUES (90708,1,140776,0,0) ON DUPLICATE KEY UPDATE `ItemID1`=VALUES(`ItemID1`), `ItemID2`=VALUES(`ItemID2`), `ItemID3`=VALUES(`ItemID3`);
INSERT INTO `creature_equip_template` (`CreatureID`,`ID`,`ItemID1`,`ItemID2`,`ItemID3`) VALUES (90709,1,2179,0,0) ON DUPLICATE KEY UPDATE `ItemID1`=VALUES(`ItemID1`), `ItemID2`=VALUES(`ItemID2`), `ItemID3`=VALUES(`ItemID3`);
INSERT INTO `creature_equip_template` (`CreatureID`,`ID`,`ItemID1`,`ItemID2`,`ItemID3`) VALUES (90710,1,58367,0,0) ON DUPLICATE KEY UPDATE `ItemID1`=VALUES(`ItemID1`), `ItemID2`=VALUES(`ItemID2`), `ItemID3`=VALUES(`ItemID3`);
INSERT INTO `creature_equip_template` (`CreatureID`,`ID`,`ItemID1`,`ItemID2`,`ItemID3`) VALUES (90711,1,140777,0,0) ON DUPLICATE KEY UPDATE `ItemID1`=VALUES(`ItemID1`), `ItemID2`=VALUES(`ItemID2`), `ItemID3`=VALUES(`ItemID3`);
INSERT INTO `creature_equip_template` (`CreatureID`,`ID`,`ItemID1`,`ItemID2`,`ItemID3`) VALUES (90712,1,140546,0,0) ON DUPLICATE KEY UPDATE `ItemID1`=VALUES(`ItemID1`), `ItemID2`=VALUES(`ItemID2`), `ItemID3`=VALUES(`ItemID3`);
INSERT INTO `creature_equip_template` (`CreatureID`,`ID`,`ItemID1`,`ItemID2`,`ItemID3`) VALUES (90713,1,45899,0,0) ON DUPLICATE KEY UPDATE `ItemID1`=VALUES(`ItemID1`), `ItemID2`=VALUES(`ItemID2`), `ItemID3`=VALUES(`ItemID3`);
INSERT INTO `creature_equip_template` (`CreatureID`,`ID`,`ItemID1`,`ItemID2`,`ItemID3`) VALUES (90714,1,139131,0,12869) ON DUPLICATE KEY UPDATE `ItemID1`=VALUES(`ItemID1`), `ItemID2`=VALUES(`ItemID2`), `ItemID3`=VALUES(`ItemID3`);
INSERT INTO `creature_equip_template` (`CreatureID`,`ID`,`ItemID1`,`ItemID2`,`ItemID3`) VALUES (90716,1,53096,0,11587) ON DUPLICATE KEY UPDATE `ItemID1`=VALUES(`ItemID1`), `ItemID2`=VALUES(`ItemID2`), `ItemID3`=VALUES(`ItemID3`);
INSERT INTO `creature_equip_template` (`CreatureID`,`ID`,`ItemID1`,`ItemID2`,`ItemID3`) VALUES (90717,1,139132,0,0) ON DUPLICATE KEY UPDATE `ItemID1`=VALUES(`ItemID1`), `ItemID2`=VALUES(`ItemID2`), `ItemID3`=VALUES(`ItemID3`);
INSERT INTO `creature_equip_template` (`CreatureID`,`ID`,`ItemID1`,`ItemID2`,`ItemID3`) VALUES (91353,1,45726,0,45727) ON DUPLICATE KEY UPDATE `ItemID1`=VALUES(`ItemID1`), `ItemID2`=VALUES(`ItemID2`), `ItemID3`=VALUES(`ItemID3`);
INSERT INTO `creature_equip_template` (`CreatureID`,`ID`,`ItemID1`,`ItemID2`,`ItemID3`) VALUES (91949,1,0,0,0) ON DUPLICATE KEY UPDATE `ItemID1`=VALUES(`ItemID1`), `ItemID2`=VALUES(`ItemID2`), `ItemID3`=VALUES(`ItemID3`);
INSERT INTO `creature_equip_template` (`CreatureID`,`ID`,`ItemID1`,`ItemID2`,`ItemID3`) VALUES (91951,1,13262,0,0) ON DUPLICATE KEY UPDATE `ItemID1`=VALUES(`ItemID1`), `ItemID2`=VALUES(`ItemID2`), `ItemID3`=VALUES(`ItemID3`);
INSERT INTO `creature_equip_template` (`CreatureID`,`ID`,`ItemID1`,`ItemID2`,`ItemID3`) VALUES (92074,1,73008,0,0) ON DUPLICATE KEY UPDATE `ItemID1`=VALUES(`ItemID1`), `ItemID2`=VALUES(`ItemID2`), `ItemID3`=VALUES(`ItemID3`);
INSERT INTO `creature_equip_template` (`CreatureID`,`ID`,`ItemID1`,`ItemID2`,`ItemID3`) VALUES (92122,1,0,0,0) ON DUPLICATE KEY UPDATE `ItemID1`=VALUES(`ItemID1`), `ItemID2`=VALUES(`ItemID2`), `ItemID3`=VALUES(`ItemID3`);
INSERT INTO `creature_equip_template` (`CreatureID`,`ID`,`ItemID1`,`ItemID2`,`ItemID3`) VALUES (92586,1,0,0,0) ON DUPLICATE KEY UPDATE `ItemID1`=VALUES(`ItemID1`), `ItemID2`=VALUES(`ItemID2`), `ItemID3`=VALUES(`ItemID3`);
INSERT INTO `creature_equip_template` (`CreatureID`,`ID`,`ItemID1`,`ItemID2`,`ItemID3`) VALUES (93704,1,13631,0,0) ON DUPLICATE KEY UPDATE `ItemID1`=VALUES(`ItemID1`), `ItemID2`=VALUES(`ItemID2`), `ItemID3`=VALUES(`ItemID3`);
INSERT INTO `creature_equip_template` (`CreatureID`,`ID`,`ItemID1`,`ItemID2`,`ItemID3`) VALUES (94209,1,1899,0,117413) ON DUPLICATE KEY UPDATE `ItemID1`=VALUES(`ItemID1`), `ItemID2`=VALUES(`ItemID2`), `ItemID3`=VALUES(`ItemID3`);
INSERT INTO `creature_equip_template` (`CreatureID`,`ID`,`ItemID1`,`ItemID2`,`ItemID3`) VALUES (94223,1,40595,0,40596) ON DUPLICATE KEY UPDATE `ItemID1`=VALUES(`ItemID1`), `ItemID2`=VALUES(`ItemID2`), `ItemID3`=VALUES(`ItemID3`);
INSERT INTO `creature_equip_template` (`CreatureID`,`ID`,`ItemID1`,`ItemID2`,`ItemID3`) VALUES (97496,1,45726,0,45727) ON DUPLICATE KEY UPDATE `ItemID1`=VALUES(`ItemID1`), `ItemID2`=VALUES(`ItemID2`), `ItemID3`=VALUES(`ItemID3`);
INSERT INTO `creature_equip_template` (`CreatureID`,`ID`,`ItemID1`,`ItemID2`,`ItemID3`) VALUES (97502,1,40595,0,40596) ON DUPLICATE KEY UPDATE `ItemID1`=VALUES(`ItemID1`), `ItemID2`=VALUES(`ItemID2`), `ItemID3`=VALUES(`ItemID3`);
INSERT INTO `creature_equip_template` (`CreatureID`,`ID`,`ItemID1`,`ItemID2`,`ItemID3`) VALUES (97503,1,1899,0,117413) ON DUPLICATE KEY UPDATE `ItemID1`=VALUES(`ItemID1`), `ItemID2`=VALUES(`ItemID2`), `ItemID3`=VALUES(`ItemID3`);
INSERT INTO `creature_equip_template` (`CreatureID`,`ID`,`ItemID1`,`ItemID2`,`ItemID3`) VALUES (97521,1,140546,0,0) ON DUPLICATE KEY UPDATE `ItemID1`=VALUES(`ItemID1`), `ItemID2`=VALUES(`ItemID2`), `ItemID3`=VALUES(`ItemID3`);
INSERT INTO `creature_equip_template` (`CreatureID`,`ID`,`ItemID1`,`ItemID2`,`ItemID3`) VALUES (97525,1,12754,0,0) ON DUPLICATE KEY UPDATE `ItemID1`=VALUES(`ItemID1`), `ItemID2`=VALUES(`ItemID2`), `ItemID3`=VALUES(`ItemID3`);
INSERT INTO `creature_equip_template` (`CreatureID`,`ID`,`ItemID1`,`ItemID2`,`ItemID3`) VALUES (97526,1,5286,0,5286) ON DUPLICATE KEY UPDATE `ItemID1`=VALUES(`ItemID1`), `ItemID2`=VALUES(`ItemID2`), `ItemID3`=VALUES(`ItemID3`);
INSERT INTO `creature_equip_template` (`CreatureID`,`ID`,`ItemID1`,`ItemID2`,`ItemID3`) VALUES (97527,1,2176,0,0) ON DUPLICATE KEY UPDATE `ItemID1`=VALUES(`ItemID1`), `ItemID2`=VALUES(`ItemID2`), `ItemID3`=VALUES(`ItemID3`);
INSERT INTO `creature_equip_template` (`CreatureID`,`ID`,`ItemID1`,`ItemID2`,`ItemID3`) VALUES (97528,1,2176,0,0) ON DUPLICATE KEY UPDATE `ItemID1`=VALUES(`ItemID1`), `ItemID2`=VALUES(`ItemID2`), `ItemID3`=VALUES(`ItemID3`);
INSERT INTO `creature_equip_template` (`CreatureID`,`ID`,`ItemID1`,`ItemID2`,`ItemID3`) VALUES (110941,1,40595,0,40596) ON DUPLICATE KEY UPDATE `ItemID1`=VALUES(`ItemID1`), `ItemID2`=VALUES(`ItemID2`), `ItemID3`=VALUES(`ItemID3`);
INSERT INTO `creature_equip_template` (`CreatureID`,`ID`,`ItemID1`,`ItemID2`,`ItemID3`) VALUES (112879,1,73008,0,0) ON DUPLICATE KEY UPDATE `ItemID1`=VALUES(`ItemID1`), `ItemID2`=VALUES(`ItemID2`), `ItemID3`=VALUES(`ItemID3`);
INSERT INTO `creature_equip_template` (`CreatureID`,`ID`,`ItemID1`,`ItemID2`,`ItemID3`) VALUES (112920,1,0,0,0) ON DUPLICATE KEY UPDATE `ItemID1`=VALUES(`ItemID1`), `ItemID2`=VALUES(`ItemID2`), `ItemID3`=VALUES(`ItemID3`);
INSERT INTO `creature_equip_template` (`CreatureID`,`ID`,`ItemID1`,`ItemID2`,`ItemID3`) VALUES (112921,1,0,0,0) ON DUPLICATE KEY UPDATE `ItemID1`=VALUES(`ItemID1`), `ItemID2`=VALUES(`ItemID2`), `ItemID3`=VALUES(`ItemID3`);
UPDATE `creature` c
JOIN `creature_equip_template` e ON e.`CreatureID` = c.`id` AND e.`ID` = 1
SET c.`equipment_id` = 1
WHERE c.`map` = 1460 AND c.`equipment_id` = 0;
+33
View File
@@ -0,0 +1,33 @@
-- =====================================================================
-- Rivage brisé — créatures posées deux fois au même endroit
--
-- SIGNALÉ EN JEU : « il est spawn 2 fois au même endroit » (entrée
-- 92564, guid 290200360).
--
-- Neuf créatures de la carte 1460 occupent une position strictement
-- identique à une autre — même entrée, mêmes coordonnées au centimètre.
-- En jeu, deux modèles se superposent exactement.
--
-- L'IMPORT N'EST PAS EN CAUSE. La référence les porte déjà en double :
-- (294825,92564,1460,…,1254.48,2438.92,47.0961,…)
-- (294827,92564,1460,…,1254.48,2438.92,47.0961,…)
-- La transposition était fidèle ; c'est la donnée d'origine qui l'est
-- moins. Écart assumé avec la référence, au bénéfice du rendu.
--
-- On conserve le plus petit guid de chaque paire.
-- =====================================================================
DELETE c FROM `creature` c
JOIN (
SELECT `id`, ROUND(`position_x`,1) AS px, ROUND(`position_y`,1) AS py,
MIN(`guid`) AS garder
FROM `creature`
WHERE `map` IN (1460, 1666)
GROUP BY `id`, px, py
HAVING COUNT(*) > 1
) AS d
ON c.`id` = d.`id`
AND ROUND(c.`position_x`,1) = d.px
AND ROUND(c.`position_y`,1) = d.py
AND c.`guid` <> d.garder
WHERE c.`map` IN (1460, 1666);
+39
View File
@@ -0,0 +1,39 @@
-- =====================================================================
-- Rivage brisé — les démons passent en faction 2898
--
-- ÉTABLI PAR TEST EN JEU, et il résout le paradoxe d'août.
--
-- En août on avait basculé tous les démons de cette carte en faction 16
-- parce que la 2780 les rendait inattaquables — sans comprendre
-- pourquoi, la théorie disant l'inverse. La 16 fonctionnait, on l'a
-- prise.
--
-- Ce qu'on ignorait, c'est le prix : la faction 16 porte
-- `EnemyGroup = 1`, elle n'est hostile QU'AUX JOUEURS. Nos alliés, eux,
-- ont `EnemyGroup = 0` et une liste d'ennemis qui désigne la faction
-- 1786. Alliés et démons ne se reconnaissaient donc pas — d'où les
-- soldats qui entrent en combat puis n'ont personne à frapper, relevé
-- par la sonde : « liste de menace vide », onze fois pour la garde
-- royale gilnéenne, dix fois pour Jaina, dix fois pour Varian.
--
-- On avait réparé le joueur en supprimant la bataille.
--
-- La 2898 porte la faction 1786 comme la 2780, mais avec
-- `EnemyGroup = 15` — hostile à tous les groupes, joueurs compris.
-- Vérifié en jeu sur le Molosse de l'effroi gangrené : attaquable.
--
-- PORTÉE : uniquement les entrées exclusives à la carte 1460, comme en
-- août. Celles partagées avec d'autres cartes sont écartées.
--
-- Retour arrière : rebasculer les mêmes entrées en 16.
-- =====================================================================
UPDATE `creature_template` ct
JOIN (SELECT DISTINCT c.`id`
FROM `creature` c
JOIN `creature_template` t ON t.`entry` = c.`id`
WHERE c.`map` = 1460
AND t.`faction` = 16
AND c.`id` NOT IN (SELECT DISTINCT `id` FROM `creature` WHERE `map` <> 1460)) AS cible
ON cible.`id` = ct.`entry`
SET ct.`faction` = 2898;
+279
View File
@@ -0,0 +1,279 @@
-- =====================================================================
-- Rivage brisé — posture et armement des alliés
--
-- SIGNALÉ EN JEU : « les PNJ ont bien leurs armes mais pas de posture
-- de combat actif ».
--
-- 131 alliés armés passent en EMOTE_STATE_READY1H (333), arme au
-- clair. Les 132 sans équipement gardent une posture neutre : leur
-- donner une attitude armée les ferait mimer une arme absente.
--
-- La table creature_addon ne contenait AUCUNE ligne pour cette carte
-- avant aujourd hui, d où l absence totale de posture.
-- =====================================================================
DELETE a FROM `creature_addon` a JOIN `creature` c ON c.`guid` = a.`guid` WHERE c.`map` = 1460;
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200017,0,0,0,1,333,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200022,0,0,0,1,0,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200023,0,0,0,1,0,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200026,0,0,0,1,333,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200027,0,0,0,1,333,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200032,0,0,0,1,0,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200035,0,0,0,1,333,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200037,0,0,0,1,0,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200038,0,0,0,1,0,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200041,0,0,0,1,333,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200042,0,0,0,1,333,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200043,0,0,0,1,0,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200044,0,0,0,1,333,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200045,0,0,0,1,333,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200046,0,0,0,1,0,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200047,0,0,0,1,0,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200048,0,0,0,1,333,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200049,0,0,0,1,0,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200050,0,0,0,1,333,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200051,0,0,0,1,0,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200052,0,0,0,1,333,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200053,0,0,0,1,0,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200054,0,0,0,1,0,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200055,0,0,0,1,0,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200058,0,0,0,1,333,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200059,0,0,0,1,333,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200060,0,0,0,1,333,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200061,0,0,0,1,0,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200062,0,0,0,1,0,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200063,0,0,0,1,333,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200064,0,0,0,1,333,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200065,0,0,0,1,0,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200067,0,0,0,1,0,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200069,0,0,0,1,0,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200071,0,0,0,1,0,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200072,0,0,0,1,333,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200073,0,0,0,1,0,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200075,0,0,0,1,0,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200077,0,0,0,1,0,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200078,0,0,0,1,0,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200083,0,0,0,1,0,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200085,0,0,0,1,0,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200086,0,0,0,1,333,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200087,0,0,0,1,0,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200088,0,0,0,1,0,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200090,0,0,0,1,333,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200091,0,0,0,1,333,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200094,0,0,0,1,333,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200095,0,0,0,1,0,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200109,0,0,0,1,0,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200115,0,0,0,1,0,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200117,0,0,0,1,0,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200119,0,0,0,1,333,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200120,0,0,0,1,0,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200121,0,0,0,1,333,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200122,0,0,0,1,333,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200123,0,0,0,1,333,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200124,0,0,0,1,333,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200125,0,0,0,1,333,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200126,0,0,0,1,333,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200127,0,0,0,1,333,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200128,0,0,0,1,333,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200129,0,0,0,1,333,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200130,0,0,0,1,333,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200131,0,0,0,1,333,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200132,0,0,0,1,333,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200133,0,0,0,1,0,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200134,0,0,0,1,333,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200135,0,0,0,1,333,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200136,0,0,0,1,333,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200137,0,0,0,1,333,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200138,0,0,0,1,333,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200139,0,0,0,1,0,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200145,0,0,0,1,333,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200156,0,0,0,1,0,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200164,0,0,0,1,0,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200167,0,0,0,1,0,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200168,0,0,0,1,0,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200169,0,0,0,1,0,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200186,0,0,0,1,0,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200223,0,0,0,1,0,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200224,0,0,0,1,0,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200225,0,0,0,1,0,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200226,0,0,0,1,0,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200227,0,0,0,1,0,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200228,0,0,0,1,0,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200229,0,0,0,1,0,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200230,0,0,0,1,0,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200231,0,0,0,1,0,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200232,0,0,0,1,0,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200233,0,0,0,1,0,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200234,0,0,0,1,333,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200235,0,0,0,1,333,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200236,0,0,0,1,0,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200237,0,0,0,1,333,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200238,0,0,0,1,333,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200239,0,0,0,1,0,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200240,0,0,0,1,0,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200241,0,0,0,1,0,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200242,0,0,0,1,0,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200243,0,0,0,1,333,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200244,0,0,0,1,333,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200245,0,0,0,1,333,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200246,0,0,0,1,333,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200247,0,0,0,1,333,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200248,0,0,0,1,0,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200249,0,0,0,1,0,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200295,0,0,0,1,0,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200322,0,0,0,1,333,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200323,0,0,0,1,333,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200324,0,0,0,1,0,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200325,0,0,0,1,333,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200326,0,0,0,1,0,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200327,0,0,0,1,0,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200328,0,0,0,1,333,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200330,0,0,0,1,0,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200331,0,0,0,1,333,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200332,0,0,0,1,333,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200333,0,0,0,1,0,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200334,0,0,0,1,0,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200335,0,0,0,1,333,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200336,0,0,0,1,333,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200337,0,0,0,1,333,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200338,0,0,0,1,333,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200339,0,0,0,1,333,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200340,0,0,0,1,0,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200341,0,0,0,1,333,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200342,0,0,0,1,0,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200343,0,0,0,1,333,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200344,0,0,0,1,0,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200345,0,0,0,1,0,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200346,0,0,0,1,333,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200347,0,0,0,1,333,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200348,0,0,0,1,333,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200349,0,0,0,1,333,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200350,0,0,0,1,0,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200351,0,0,0,1,333,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200352,0,0,0,1,333,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200353,0,0,0,1,333,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200354,0,0,0,1,333,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200355,0,0,0,1,333,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200356,0,0,0,1,333,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200357,0,0,0,1,333,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200359,0,0,0,1,333,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200363,0,0,0,1,0,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200364,0,0,0,1,0,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200365,0,0,0,1,0,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200366,0,0,0,1,0,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200367,0,0,0,1,0,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200368,0,0,0,1,0,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200369,0,0,0,1,0,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200370,0,0,0,1,333,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200371,0,0,0,1,0,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200372,0,0,0,1,0,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200373,0,0,0,1,333,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200374,0,0,0,1,333,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200375,0,0,0,1,333,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200376,0,0,0,1,0,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200377,0,0,0,1,333,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200378,0,0,0,1,333,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200379,0,0,0,1,333,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200380,0,0,0,1,333,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200381,0,0,0,1,333,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200382,0,0,0,1,333,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200383,0,0,0,1,333,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200402,0,0,0,1,333,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200403,0,0,0,1,0,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200404,0,0,0,1,333,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200405,0,0,0,1,333,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200406,0,0,0,1,0,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200407,0,0,0,1,333,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200408,0,0,0,1,333,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200409,0,0,0,1,0,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200410,0,0,0,1,333,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200411,0,0,0,1,333,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200412,0,0,0,1,0,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200413,0,0,0,1,333,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200414,0,0,0,1,333,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200415,0,0,0,1,0,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200416,0,0,0,1,333,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200417,0,0,0,1,333,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200418,0,0,0,1,333,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200420,0,0,0,1,0,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200421,0,0,0,1,0,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200422,0,0,0,1,0,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200423,0,0,0,1,0,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200424,0,0,0,1,0,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200425,0,0,0,1,0,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200426,0,0,0,1,0,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200427,0,0,0,1,0,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200428,0,0,0,1,0,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200429,0,0,0,1,0,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200430,0,0,0,1,0,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200433,0,0,0,1,0,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200434,0,0,0,1,0,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200435,0,0,0,1,0,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200436,0,0,0,1,333,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200443,0,0,0,1,333,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200444,0,0,0,1,333,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200526,0,0,0,1,0,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200527,0,0,0,1,0,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200528,0,0,0,1,0,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200573,0,0,0,1,0,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200599,0,0,0,1,0,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200632,0,0,0,1,0,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200642,0,0,0,1,0,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200644,0,0,0,1,333,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200645,0,0,0,1,333,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200646,0,0,0,1,0,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200647,0,0,0,1,0,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200648,0,0,0,1,333,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200649,0,0,0,1,0,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200650,0,0,0,1,0,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200651,0,0,0,1,333,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200652,0,0,0,1,333,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200653,0,0,0,1,0,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200654,0,0,0,1,333,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200655,0,0,0,1,0,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200656,0,0,0,1,0,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200657,0,0,0,1,333,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200658,0,0,0,1,0,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200664,0,0,0,1,0,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200665,0,0,0,1,0,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200666,0,0,0,1,0,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200667,0,0,0,1,333,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200668,0,0,0,1,0,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200669,0,0,0,1,333,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200670,0,0,0,1,0,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200671,0,0,0,1,333,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200672,0,0,0,1,0,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200673,0,0,0,1,0,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200674,0,0,0,1,333,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200675,0,0,0,1,0,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200677,0,0,0,1,333,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200678,0,0,0,1,333,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200679,0,0,0,1,333,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200680,0,0,0,1,0,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200681,0,0,0,1,0,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200682,0,0,0,1,0,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200683,0,0,0,1,0,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200685,0,0,0,1,0,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200690,0,0,0,1,333,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200714,0,0,0,1,0,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200716,0,0,0,1,0,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200717,0,0,0,1,333,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200718,0,0,0,1,0,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200720,0,0,0,1,333,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200722,0,0,0,1,333,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200725,0,0,0,1,0,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200731,0,0,0,1,333,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200733,0,0,0,1,0,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200734,0,0,0,1,333,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200742,0,0,0,1,333,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200743,0,0,0,1,333,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200744,0,0,0,1,333,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200745,0,0,0,1,333,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200746,0,0,0,1,333,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200747,0,0,0,1,333,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200748,0,0,0,1,333,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200749,0,0,0,1,333,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200750,0,0,0,1,333,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200751,0,0,0,1,333,0,0,0,0,"");
INSERT INTO `creature_addon` (`guid`,`path_id`,`mount`,`bytes1`,`bytes2`,`emote`,`aiAnimKit`,`movementAnimKit`,`meleeAnimKit`,`visibilityDistanceType`,`auras`) VALUES (290200752,0,0,0,1,333,0,0,0,0,"");
@@ -0,0 +1,27 @@
-- Objet 133995 "Recette du breuvage des tempetes" : enregistrement DB2 custom.
--
-- La quete 41039 "Un savoir vole" (Suramar) exige cet objet, lache par Morjirn (102852).
-- L'objet existe dans Item.db2 mais PAS dans ItemSparse.db2 : le core ne construit un
-- ItemTemplate que si les deux sont presents (ObjectMgr::LoadItemTemplates), donc le
-- serveur l'ignorait et rejetait toute ligne de butin le referencant :
-- Table 'creature_loot_template' Entry 102852 Item 133995: item entry not listed
-- in `item_template` - skipped
-- Verifie : 104 011 ItemTemplate charges pour 103 967 enregistrements ItemSparse, donc
-- le fichier est bien lu en entier et l'objet en est reellement absent.
--
-- Methode : ligne dans la table miroir + ligne hotfix_data (sans quoi l'enregistrement
-- n'existerait que cote serveur) + bump de HotfixCacheVersion pour invalider le cache
-- ADB des clients. TableHash d'ItemSparse.db2 = 0x919BE54E, lu a l'offset 20 de l'en-tete WDC1.
--
-- Nom en francais dans la colonne de base : le serveur ne sert que la locale enUS
-- (DBC.Locale = 0), donc item_sparse_locale ne serait jamais consulte. Le libelle reprend
-- celui de l'objectif de quete traduit : "Recette du breuvage des tempetes".
DELETE FROM `item_sparse` WHERE `ID` = 133995;
INSERT INTO `item_sparse`
(`ID`,`AllowableRace`,`AllowableClass`,`Display`,`OverallQualityID`,`ItemLevel`,`Stackable`,`MaxCount`,`Bonding`,`ExpansionID`,`VerifiedBuild`)
VALUES
(133995, -1, -1, 'Recette du breuvage des tempêtes', 1, 1, 1, 1, 4, 6, 0);
DELETE FROM `hotfix_data` WHERE `TableHash` = 2442913102 AND `RecordId` = 133995;
INSERT INTO `hotfix_data` (`Id`,`TableHash`,`RecordId`,`Deleted`) VALUES (391, 2442913102, 133995, 0);
@@ -0,0 +1,25 @@
-- Quete 36382 "Coup d'oeil dans le portail" (Horde) : tout le cote Horde manquait.
--
-- Signalement joueur : impossible d'utiliser le telescope, et l'eclaireuse a cote attaque.
-- Cause : Scout Pazaztick (85249) et le telescope Horde (234619) n'ont AUCUN spawn en base.
-- Seule la paire Alliance existe (Scout Pazerp 85212 + telescope 234576, map 1190 area 6963).
-- Un joueur Horde qui prend la quete chez Rokhan arrive donc sur la paire Alliance :
-- - le telescope 234576 ne valide que l'objectif de la quete 36379 (Alliance) ;
-- - Scout Pazerp est en faction 84 (Hurlevent), donc hostile a la Horde et elle agresse.
-- Sur les royaumes officiels les deux paires cohabitent au meme endroit, separees par phasing.
-- La zone (map 1190) n'a aucun phasing chez nous : on partage donc le telescope existant.
-- 1) Les deux eclaireuses deviennent neutres. Ce sont des PNJ decoratifs (npcflag 0,
-- aucun gossip, aucun role de combat) : leur faction ne sert qu'a les faire agresser.
UPDATE `creature_template` SET `faction` = 35 WHERE `entry` IN (85212, 85249);
-- 2) Le telescope deja spawne sert aux deux factions.
-- Data1 = quete requise pour valider le goober : a 36379 il bloquait tout joueur Horde.
UPDATE `gameobject_template` SET `Data1` = 0 WHERE `entry` = 234576;
UPDATE `quest_objectives` SET `ObjectID` = 234576 WHERE `ID` = 274669 AND `QuestID` = 36382;
-- 3) Scout Pazaztick est spawnee a cote de Scout Pazerp (le journal de quete demande de la
-- trouver ; elle ne porte aucun objectif, c'est le telescope qui valide).
DELETE FROM `creature` WHERE `guid` = 290200757;
INSERT INTO `creature` (`guid`,`id`,`map`,`zoneId`,`areaId`,`spawnDifficulties`,`phaseUseFlags`,`PhaseId`,`PhaseGroup`,`terrainSwapMap`,`modelid`,`equipment_id`,`position_x`,`position_y`,`position_z`,`orientation`,`spawntimesecs`,`spawndist`,`currentwaypoint`,`curhealth`,`curmana`,`MovementType`,`npcflag`,`unit_flags`,`unit_flags2`,`unit_flags3`,`dynamicflags`,`ScriptName`,`movementmode`,`VerifiedBuild`) VALUES
(290200757, 85249, 1190, 4, 6963, '0', 0, 0, 0, -1, 0, 0, -11474.94, -3450.80, 32.7659, 2.03782, 120, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, '', 0, 0);
@@ -0,0 +1,13 @@
-- Retour dans un fief deja fonde.
--
-- Les deux points d'entree du fief WoD ne se declenchent que si le joueur n'a
-- PAS encore de fief : l'objet Master Surveyor (233664, Horde) et Baros
-- Alexston (79243, Alliance). Une fois le fief fonde, plus rien ne permettait
-- d'y entrer. Les trois joueurs du royaume qui possedent un fief ne sont
-- jamais alles dessus (aucun personnage n'a jamais ete sur les cartes 1152,
-- 1153, 1158, 1159, 1330 ni 1331).
--
-- Gazlowe (78466) recoit donc un script qui propose "Emmenez-moi a mon fief"
-- aux proprietaires. Il est aussi spawne DANS les fiefs (cartes 1152, 1153,
-- 1330) : le script n'y propose rien, il teste Map::IsGarrison().
UPDATE `creature_template` SET `ScriptName` = 'npc_gazlowe_garrison' WHERE `entry` = 78466;
@@ -0,0 +1,76 @@
-- Butin manquant : mobs des quetes d'acces a Draenor (Terres foudroyees 6.0 + Tanaan).
--
-- Signalement joueur du 05/09/2026 : "les mobs lachent rien comme loot".
-- 26 especes hostiles de ces deux zones avaient lootid = 0, soit aucun butin du tout,
-- alors que leurs voisines immediates (Fourrageur, Legionnaire, Pillard...) ont la leur.
--
-- Taux releves un par un sur Wowhead (page rendue puis donnees Listview, methode validee
-- en recoupant deux PNJ dont les chiffres avaient d'abord ete lus a l'ecran).
-- Les objets post-Legion listes par Wowhead (Besaces et Potions mysterieuses, ids 235xxx
-- et 236xxx) sont volontairement ecartes : ils n'existent pas dans notre version.
--
-- 9 des 26 especes n'ont legitimement aucun butin sur Wowhead non plus et ne sont pas
-- touchees : Ame tourmentee (82647), Loup du Vide (82373), Demolisseur de fer (82298),
-- Gronnling de fer (82484), Lieutenant Asalra (76448), Keli'dan le Briseur (79702),
-- Ankova la Dechue (79593), Bagarreur de la Main brisee (82057), et le Sauvage Ombresanglante
-- (78820), dont le seul objet liste (110444) n'est rattache a aucune quete chez nous.
DELETE FROM `creature_loot_template` WHERE `Entry` IN (76556,76886,78345,78489,78674,78348,78670,77653,82774,77767,76651,78488,77790,77771,77845,78696,82451);
INSERT INTO `creature_loot_template`
(`Entry`,`Item`,`Reference`,`Chance`,`QuestRequired`,`LootMode`,`GroupId`,`MinCount`,`MaxCount`,`Comment`) VALUES
(76556,112463,0,76.6200,0,1,0,1,1,'Ironmarch Grunt'),
(76556,112684,0,31.1900,0,1,0,1,1,'Ironmarch Grunt'),
(76556,118675,0,0.4100,0,1,0,1,1,'Ironmarch Grunt'),
(76886,112463,0,76.8200,0,1,0,1,1,'Ironmarch Scout'),
(76886,112684,0,30.5500,0,1,0,1,1,'Ironmarch Scout'),
(76886,118675,0,0.3100,0,1,0,1,1,'Ironmarch Scout'),
(78345,112463,0,77.6600,0,1,0,1,1,'Dreadmaul Crusher'),
(78345,112684,0,29.1100,0,1,0,1,1,'Dreadmaul Crusher'),
(78345,81194,0,0.7700,0,1,0,1,1,'Dreadmaul Crusher'),
(78345,118675,0,0.2300,0,1,0,1,1,'Dreadmaul Crusher'),
(78345,81212,0,0.0200,0,1,0,1,1,'Dreadmaul Crusher'),
(78489,81194,0,94.0100,0,1,0,1,1,'Snickerfang Pup'),
(78489,81212,0,6.0000,0,1,0,1,1,'Snickerfang Pup'),
(78489,112463,0,0.2000,0,1,0,1,1,'Snickerfang Pup'),
(78674,112463,0,99.7200,0,1,0,1,1,'Ironmarch Scorcher'),
(78674,112684,0,37.2400,0,1,0,1,1,'Ironmarch Scorcher'),
(78674,44754,0,0.0300,0,1,0,1,1,'Ironmarch Scorcher'),
(78674,118675,0,0.0200,0,1,0,1,1,'Ironmarch Scorcher'),
(78348,112463,0,76.2600,0,1,0,1,1,'Dreadmaul Flamebelcher'),
(78348,112684,0,32.1200,0,1,0,1,1,'Dreadmaul Flamebelcher'),
(78348,118675,0,0.1700,0,1,0,1,1,'Dreadmaul Flamebelcher'),
(78348,81212,0,0.0600,0,1,0,1,1,'Dreadmaul Flamebelcher'),
(78670,112463,0,99.7500,0,1,0,1,1,'Ironmarch Warcaster'),
(78670,112684,0,38.0700,0,1,0,1,1,'Ironmarch Warcaster'),
(78670,118675,0,0.2300,0,1,0,1,1,'Ironmarch Warcaster'),
(77653,112463,0,75.1500,0,1,0,1,1,'Ironmarch Warsmith'),
(77653,112684,0,31.4200,0,1,0,1,1,'Ironmarch Warsmith'),
(77653,118675,0,0.2600,0,1,0,1,1,'Ironmarch Warsmith'),
(82774,112463,0,76.6000,0,1,0,1,1,'Ironmarch Executioner'),
(82774,112684,0,31.0100,0,1,0,1,1,'Ironmarch Executioner'),
(82774,118675,0,0.2400,0,1,0,1,1,'Ironmarch Executioner'),
(82774,2295,0,0.0400,0,1,0,1,1,'Ironmarch Executioner'),
(82774,10593,0,0.0100,0,1,0,1,1,'Ironmarch Executioner'),
(77767,112463,0,77.9300,0,1,0,1,1,'Ironmarch Shaman'),
(77767,112684,0,29.9700,0,1,0,1,1,'Ironmarch Shaman'),
(77767,118675,0,0.2200,0,1,0,1,1,'Ironmarch Shaman'),
(76651,112463,0,78.8100,0,1,0,1,1,'Ironmarch Leadspitter'),
(76651,112684,0,28.9600,0,1,0,1,1,'Ironmarch Leadspitter'),
(78488,112463,0,74.4000,0,1,0,1,1,'Dreadmaul Packmaster'),
(78488,112684,0,30.9600,0,1,0,1,1,'Dreadmaul Packmaster'),
(78488,81194,0,3.9900,0,1,0,1,1,'Dreadmaul Packmaster'),
(78488,81212,0,1.8800,0,1,0,1,1,'Dreadmaul Packmaster'),
(78488,118675,0,0.2200,0,1,0,1,1,'Dreadmaul Packmaster'),
(77790,112463,0,77.4700,0,1,0,1,1,'Ironmarch Raider'),
(77790,112684,0,31.6200,0,1,0,1,1,'Ironmarch Raider'),
(77771,112463,0,77.4700,0,1,0,1,1,'Dreadmaul Destroyer'),
(77771,112684,0,28.6900,0,1,0,1,1,'Dreadmaul Destroyer'),
(77845,106870,0,88.0300,0,1,0,1,1,'Ironmarch War Wolf'),
(77845,106889,0,9.7600,0,1,0,1,1,'Ironmarch War Wolf'),
(77845,106873,0,2.0200,0,1,0,1,1,'Ironmarch War Wolf'),
(78696,112463,0,99.4700,0,1,0,1,1,'Ironmarch Champion'),
(78696,112684,0,41.2500,0,1,0,1,1,'Ironmarch Champion'),
(82451,112463,0,99.6100,0,1,0,1,1,'Toothsmash the Annihilator'),
(82451,112684,0,38.3200,0,1,0,1,1,'Toothsmash the Annihilator');
UPDATE `creature_template` SET `lootid` = `entry` WHERE `entry` IN (76556,76886,78345,78489,78674,78348,78670,77653,82774,77767,76651,78488,77790,77771,77845,78696,82451);
@@ -0,0 +1,83 @@
-- Butin manquant : suite du Draenor (carte 1116), apres les zones d'acces.
--
-- 245 especes hostiles avaient lootid = 0. Relevees une par une sur Wowhead avec
-- controle du statut HTTP 200 et du marqueur g_npcs de la fiche : sans ce controle,
-- une page de refus 403 se lit exactement comme un PNJ sans butin (8 refus rencontres).
--
-- Resultat : 222 des 245 n'ont effectivement aucun butin sur les royaumes officiels
-- non plus (decors, critters, PNJ scenarises, envahisseurs de fief). 8 autres n'ont
-- que des objets post-Legion, absents de notre Item.db2. Restent 15 especes.
--
-- Les materiaux type Fourrure somptueuse (111557) sont bien du butin de cadavre ici :
-- ils apparaissent 196 fois dans creature_loot_template et jamais dans skinning_loot_template.
DELETE FROM `creature_loot_template` WHERE `Entry` IN (73227,73805,74414,76663,76686,77091,77522,78458,78662,78665,78700,80192,82515,82728,86443);
INSERT INTO `creature_loot_template`
(`Entry`,`Item`,`Reference`,`Chance`,`QuestRequired`,`LootMode`,`GroupId`,`MinCount`,`MaxCount`,`Comment`) VALUES
(73227,106870,0,3.1100,0,1,0,1,1,'Wastes Rockworm'),
(73227,106875,0,0.1400,0,1,0,1,1,'Wastes Rockworm'),
(73227,106877,0,0.0900,0,1,0,1,1,'Wastes Rockworm'),
(73227,109131,0,0.7000,0,1,0,1,1,'Wastes Rockworm'),
(73227,111557,0,1.1200,0,1,0,1,1,'Wastes Rockworm'),
(73805,107528,0,1.6200,0,1,0,1,1,'Corrupted Toad'),
(73805,108906,0,0.8400,0,1,0,1,1,'Corrupted Toad'),
(73805,111557,0,0.1700,0,1,0,1,1,'Corrupted Toad'),
(73805,116703,0,0.8400,0,1,0,1,1,'Corrupted Toad'),
(74414,106297,0,0.5200,0,1,0,1,1,'Juvenile Bonestripper'),
(74414,106317,0,1.2200,0,1,0,1,1,'Juvenile Bonestripper'),
(74414,107603,0,3.6700,0,1,0,1,1,'Juvenile Bonestripper'),
(76663,113000,0,4.6200,0,1,0,1,1,'Shadow Infiltrator'),
(76663,113006,0,33.8500,0,1,0,1,1,'Shadow Infiltrator'),
(76663,113007,0,9.2300,0,1,0,1,1,'Shadow Infiltrator'),
(76686,112995,0,6.5200,0,1,0,1,1,'Shadow Pillager'),
(76686,113004,0,13.0400,0,1,0,1,1,'Shadow Pillager'),
(76686,113007,0,6.5200,0,1,0,1,1,'Shadow Pillager'),
(77091,106825,0,15.3800,0,1,0,1,1,'Wild Rylak'),
(77091,107528,0,76.9200,0,1,0,1,1,'Wild Rylak'),
(77091,109133,0,46.1500,0,1,0,1,1,'Wild Rylak'),
(77091,111557,0,38.4600,0,1,0,1,1,'Wild Rylak'),
(77522,106291,0,1.9500,0,1,0,1,1,'Gul''var Peon'),
(77522,106298,0,0.3500,0,1,0,1,1,'Gul''var Peon'),
(77522,106301,0,0.8900,0,1,0,1,1,'Gul''var Peon'),
(77522,106302,0,0.1800,0,1,0,1,1,'Gul''var Peon'),
(77522,106312,0,1.9500,0,1,0,1,1,'Gul''var Peon'),
(77522,106322,0,0.8900,0,1,0,1,1,'Gul''var Peon'),
(77522,106323,0,0.1800,0,1,0,1,1,'Gul''var Peon'),
(77522,106324,0,1.0600,0,1,0,1,1,'Gul''var Peon'),
(77522,106489,0,0.8900,0,1,0,1,1,'Gul''var Peon'),
(77522,106540,0,0.1800,0,1,0,1,1,'Gul''var Peon'),
(77522,106556,0,0.1800,0,1,0,1,1,'Gul''var Peon'),
(77522,106574,0,0.4100,0,1,0,1,1,'Gul''var Peon'),
(77522,106581,0,0.1800,0,1,0,1,1,'Gul''var Peon'),
(77522,106583,0,0.3500,0,1,0,1,1,'Gul''var Peon'),
(77522,106870,0,1.6500,0,1,0,1,1,'Gul''var Peon'),
(77522,111364,0,1.1200,0,1,0,1,1,'Gul''var Peon'),
(77522,111387,0,0.4100,0,1,0,1,1,'Gul''var Peon'),
(77522,111557,0,0.3500,0,1,0,1,1,'Gul''var Peon'),
(77522,113295,0,0.3500,0,1,0,1,1,'Gul''var Peon'),
(77522,113478,0,1.1200,0,1,0,1,1,'Gul''var Peon'),
(77522,116709,0,0.3500,0,1,0,1,1,'Gul''var Peon'),
(78458,106867,0,8.4500,0,1,0,1,1,'Abyssal Invader'),
(78458,116122,0,16.4300,0,1,0,1,1,'Abyssal Invader'),
(78662,106459,0,22.7300,0,1,0,1,1,'Thunderlord Cutter'),
(78662,107603,0,4.5500,0,1,0,1,1,'Thunderlord Cutter'),
(78665,106304,0,1.9600,0,1,0,1,1,'Thunderlord Shaman'),
(78665,106472,0,1.9600,0,1,0,1,1,'Thunderlord Shaman'),
(78665,106876,0,5.8800,0,1,0,1,1,'Thunderlord Shaman'),
(78665,113381,0,4.5800,0,1,0,1,1,'Thunderlord Shaman'),
(78700,106295,0,2.1300,0,1,0,1,1,'Thunderlord Provisioner'),
(78700,106464,0,23.4000,0,1,0,1,1,'Thunderlord Provisioner'),
(80192,111953,0,4.5000,0,1,0,1,1,'Icecave Bat'),
(82515,106824,0,27.7400,0,1,0,1,1,'Darktalon Hatchling'),
(82515,106825,0,18.0600,0,1,0,1,1,'Darktalon Hatchling'),
(82728,106870,0,33.2800,0,1,0,1,1,'Agitated Piglet'),
(82728,107517,0,13.5500,0,1,0,1,1,'Agitated Piglet'),
(82728,108977,0,18.1800,0,1,0,1,1,'Agitated Piglet'),
(82728,109136,0,19.7300,0,1,0,1,1,'Agitated Piglet'),
(82728,111557,0,13.8900,0,1,0,1,1,'Agitated Piglet'),
(82728,118200,0,3.6000,0,1,0,1,1,'Agitated Piglet'),
(82728,118281,0,1.0300,0,1,0,1,1,'Agitated Piglet'),
(86443,106315,0,2.1300,0,1,0,1,1,'Ogron Pitfighter'),
(86443,106735,0,6.3800,0,1,0,1,1,'Ogron Pitfighter');
UPDATE `creature_template` SET `lootid` = `entry` WHERE `entry` IN (73227,73805,74414,76663,76686,77091,77522,78458,78662,78665,78700,80192,82515,82728,86443);
@@ -0,0 +1,260 @@
-- Butin manquant : Iles Brisees (carte 1220).
--
-- 102 especes hostiles avaient lootid = 0 (filtre affine : HealthModifier >= 1, qui
-- ecarte les decors et critters ; sans lui le comptage brut donnait 124 especes).
-- Relevees sur Wowhead avec controle du statut HTTP 200 et du marqueur g_npcs.
--
-- Wowhead agrege le jeu MODERNE : ces PNJ existent toujours et lachent des objets
-- d extensions posterieures. Sur 3727 lignes brutes relevees, seules 235 survivent
-- a trois filtres : objet present dans notre Item.db2, taux >= 1 pour cent, et objet
-- deja utilise comme butin de cadavre ailleurs dans nos tables.
--
-- Ce dernier filtre ecarte deux categories a risque : les materiaux a dominante
-- depecage (124113 Cuir de rochepeau, 124115, 129888, 129903, 134808) qui relevent
-- de skinning_loot_template, et les objets de quete jamais utilises chez nous
-- (147356 Balise de Sentinax brisee, 146684, 133995, 144362, 147396, 142079...) qui
-- exigeraient QuestRequired = 1 et une quete correspondante.
--
-- Objet dominant cote humanoides : 124437 Soie shal'dorei, a environ 50 pour cent.
-- C'est l'equivalent Legion des Fragments d'armure abimes du Draenor.
DELETE FROM `creature_loot_template` WHERE `Entry` IN (98805,102106,106798,109702,110346,111454,111463,111632,111638,111674,111821,111823,113388,115054,115431,115434,115601,115646,115648,116427,116466,116468,116469,116470,116472,118264,118294,118657,118660,118679,118680,118683,118943,119139,120342);
INSERT INTO `creature_loot_template`
(`Entry`,`Item`,`Reference`,`Chance`,`QuestRequired`,`LootMode`,`GroupId`,`MinCount`,`MaxCount`,`Comment`) VALUES
(98805,124437,0,48.1200,0,1,0,1,1,'Vilewing Hexer'),
(98805,121075,0,2.5100,0,1,0,1,1,'Vilewing Hexer'),
(98805,121336,0,1.6700,0,1,0,1,1,'Vilewing Hexer'),
(98805,121344,0,1.6700,0,1,0,1,1,'Vilewing Hexer'),
(98805,121340,0,1.2600,0,1,0,1,1,'Vilewing Hexer'),
(98805,138781,0,1.2600,0,1,0,1,1,'Vilewing Hexer'),
(102106,132184,0,72.1500,0,1,0,1,1,'Starving Basilisk'),
(102106,132183,0,21.8700,0,1,0,1,1,'Starving Basilisk'),
(102106,124439,0,5.5700,0,1,0,1,1,'Starving Basilisk'),
(102106,132190,0,3.8300,0,1,0,1,1,'Starving Basilisk'),
(102106,124438,0,2.1300,0,1,0,1,1,'Starving Basilisk'),
(106798,140220,0,10.4200,0,1,0,1,1,'Nora Blackfire'),
(106798,138781,0,5.2100,0,1,0,1,1,'Nora Blackfire'),
(106798,141031,0,2.0800,0,1,0,1,1,'Nora Blackfire'),
(106798,147430,0,1.0400,0,1,0,1,1,'Nora Blackfire'),
(109702,132183,0,60.7700,0,1,0,1,1,'Deepclaw'),
(109702,132190,0,23.8900,0,1,0,1,1,'Deepclaw'),
(109702,138839,0,17.5800,0,1,0,1,1,'Deepclaw'),
(109702,132184,0,14.3600,0,1,0,1,1,'Deepclaw'),
(109702,121054,0,3.1100,0,1,0,1,1,'Deepclaw'),
(110346,138839,0,17.6200,0,1,0,1,1,'Aodh Witherpetal'),
(110346,121215,0,4.3400,0,1,0,1,1,'Aodh Witherpetal'),
(110346,121355,0,3.3300,0,1,0,1,1,'Aodh Witherpetal'),
(110346,121347,0,1.3900,0,1,0,1,1,'Aodh Witherpetal'),
(110346,121168,0,1.0900,0,1,0,1,1,'Aodh Witherpetal'),
(110346,121335,0,1.0800,0,1,0,1,1,'Aodh Witherpetal'),
(110346,121173,0,1.0600,0,1,0,1,1,'Aodh Witherpetal'),
(111454,132183,0,63.7400,0,1,0,1,1,'Bestrix'),
(111454,138839,0,20.4200,0,1,0,1,1,'Bestrix'),
(111454,132190,0,18.8900,0,1,0,1,1,'Bestrix'),
(111454,132184,0,12.4300,0,1,0,1,1,'Bestrix'),
(111463,68055,0,100.0000,0,1,0,1,1,'Bulvinkel'),
(111632,121006,0,19.9100,0,1,0,1,1,'Hatecoil Crusher'),
(111632,124437,0,13.4200,0,1,0,1,1,'Hatecoil Crusher'),
(111632,121040,0,6.0400,0,1,0,1,1,'Hatecoil Crusher'),
(111632,121339,0,3.8000,0,1,0,1,1,'Hatecoil Crusher'),
(111632,141284,0,3.5800,0,1,0,1,1,'Hatecoil Crusher'),
(111632,121003,0,3.3600,0,1,0,1,1,'Hatecoil Crusher'),
(111632,121360,0,2.6800,0,1,0,1,1,'Hatecoil Crusher'),
(111632,121365,0,2.6800,0,1,0,1,1,'Hatecoil Crusher'),
(111632,121333,0,2.2400,0,1,0,1,1,'Hatecoil Crusher'),
(111632,121372,0,2.2400,0,1,0,1,1,'Hatecoil Crusher'),
(111632,121373,0,2.2400,0,1,0,1,1,'Hatecoil Crusher'),
(111632,121028,0,2.0100,0,1,0,1,1,'Hatecoil Crusher'),
(111632,121354,0,2.0100,0,1,0,1,1,'Hatecoil Crusher'),
(111632,121362,0,1.7900,0,1,0,1,1,'Hatecoil Crusher'),
(111632,121371,0,1.7900,0,1,0,1,1,'Hatecoil Crusher'),
(111632,121020,0,1.5700,0,1,0,1,1,'Hatecoil Crusher'),
(111632,121045,0,1.5700,0,1,0,1,1,'Hatecoil Crusher'),
(111632,121336,0,1.5700,0,1,0,1,1,'Hatecoil Crusher'),
(111632,121340,0,1.5700,0,1,0,1,1,'Hatecoil Crusher'),
(111632,121353,0,1.5700,0,1,0,1,1,'Hatecoil Crusher'),
(111632,121404,0,1.5700,0,1,0,1,1,'Hatecoil Crusher'),
(111632,139520,0,1.5700,0,1,0,1,1,'Hatecoil Crusher'),
(111632,121052,0,1.3400,0,1,0,1,1,'Hatecoil Crusher'),
(111632,121331,0,1.3400,0,1,0,1,1,'Hatecoil Crusher'),
(111632,121338,0,1.3400,0,1,0,1,1,'Hatecoil Crusher'),
(111632,121351,0,1.3400,0,1,0,1,1,'Hatecoil Crusher'),
(111632,121368,0,1.3400,0,1,0,1,1,'Hatecoil Crusher'),
(111632,141285,0,1.3400,0,1,0,1,1,'Hatecoil Crusher'),
(111632,121008,0,1.1200,0,1,0,1,1,'Hatecoil Crusher'),
(111632,121019,0,1.1200,0,1,0,1,1,'Hatecoil Crusher'),
(111632,121055,0,1.1200,0,1,0,1,1,'Hatecoil Crusher'),
(111632,121347,0,1.1200,0,1,0,1,1,'Hatecoil Crusher'),
(111632,121366,0,1.1200,0,1,0,1,1,'Hatecoil Crusher'),
(111638,124437,0,52.2500,0,1,0,1,1,'Hatecoil Stormweaver'),
(111638,141284,0,9.7500,0,1,0,1,1,'Hatecoil Stormweaver'),
(111638,121001,0,4.4700,0,1,0,1,1,'Hatecoil Stormweaver'),
(111638,121347,0,4.4600,0,1,0,1,1,'Hatecoil Stormweaver'),
(111638,141285,0,2.4300,0,1,0,1,1,'Hatecoil Stormweaver'),
(111638,121016,0,2.3900,0,1,0,1,1,'Hatecoil Stormweaver'),
(111638,121022,0,2.2200,0,1,0,1,1,'Hatecoil Stormweaver'),
(111638,132183,0,2.2000,0,1,0,1,1,'Hatecoil Stormweaver'),
(111638,138781,0,1.7300,0,1,0,1,1,'Hatecoil Stormweaver'),
(111638,121335,0,1.4200,0,1,0,1,1,'Hatecoil Stormweaver'),
(111638,121360,0,1.3600,0,1,0,1,1,'Hatecoil Stormweaver'),
(111638,121334,0,1.0200,0,1,0,1,1,'Hatecoil Stormweaver'),
(111674,132231,0,36.3100,0,1,0,1,1,'Cinderwing'),
(111674,132199,0,31.7900,0,1,0,1,1,'Cinderwing'),
(111674,132197,0,31.4100,0,1,0,1,1,'Cinderwing'),
(111674,138839,0,22.1700,0,1,0,1,1,'Cinderwing'),
(111674,121331,0,2.8700,0,1,0,1,1,'Cinderwing'),
(111821,124437,0,37.3900,0,1,0,1,1,'Felsworn Adept'),
(111821,140227,0,5.9100,0,1,0,1,1,'Felsworn Adept'),
(111821,121351,0,5.7000,0,1,0,1,1,'Felsworn Adept'),
(111821,121344,0,2.9200,0,1,0,1,1,'Felsworn Adept'),
(111821,121347,0,2.7100,0,1,0,1,1,'Felsworn Adept'),
(111821,121371,0,2.7100,0,1,0,1,1,'Felsworn Adept'),
(111821,121336,0,1.7400,0,1,0,1,1,'Felsworn Adept'),
(111821,121350,0,1.6700,0,1,0,1,1,'Felsworn Adept'),
(111821,121340,0,1.6000,0,1,0,1,1,'Felsworn Adept'),
(111821,121337,0,1.5300,0,1,0,1,1,'Felsworn Adept'),
(111821,121352,0,1.4600,0,1,0,1,1,'Felsworn Adept'),
(111821,132199,0,1.4600,0,1,0,1,1,'Felsworn Adept'),
(111821,121005,0,1.3900,0,1,0,1,1,'Felsworn Adept'),
(111821,121338,0,1.3900,0,1,0,1,1,'Felsworn Adept'),
(111821,121050,0,1.1800,0,1,0,1,1,'Felsworn Adept'),
(111821,121364,0,1.0400,0,1,0,1,1,'Felsworn Adept'),
(111823,124437,0,66.1700,0,1,0,1,1,'Felsworn Betrayer'),
(111823,121027,0,4.3300,0,1,0,1,1,'Felsworn Betrayer'),
(111823,121352,0,3.0100,0,1,0,1,1,'Felsworn Betrayer'),
(111823,138781,0,2.3800,0,1,0,1,1,'Felsworn Betrayer'),
(111823,121332,0,2.2700,0,1,0,1,1,'Felsworn Betrayer'),
(111823,121346,0,1.8300,0,1,0,1,1,'Felsworn Betrayer'),
(111823,121040,0,1.8000,0,1,0,1,1,'Felsworn Betrayer'),
(111823,121022,0,1.3600,0,1,0,1,1,'Felsworn Betrayer'),
(111823,121369,0,1.3600,0,1,0,1,1,'Felsworn Betrayer'),
(111823,121349,0,1.3200,0,1,0,1,1,'Felsworn Betrayer'),
(111823,121004,0,1.2800,0,1,0,1,1,'Felsworn Betrayer'),
(111823,121047,0,1.2800,0,1,0,1,1,'Felsworn Betrayer'),
(111823,121368,0,1.2800,0,1,0,1,1,'Felsworn Betrayer'),
(111823,121358,0,1.2500,0,1,0,1,1,'Felsworn Betrayer'),
(111823,140222,0,1.2500,0,1,0,1,1,'Felsworn Betrayer'),
(113388,124437,0,42.7900,0,1,0,1,1,'Squigglefin Crablord'),
(113388,138781,0,29.1800,0,1,0,1,1,'Squigglefin Crablord'),
(113388,121349,0,4.7400,0,1,0,1,1,'Squigglefin Crablord'),
(113388,121345,0,2.0700,0,1,0,1,1,'Squigglefin Crablord'),
(113388,121358,0,1.5600,0,1,0,1,1,'Squigglefin Crablord'),
(113388,121028,0,1.5400,0,1,0,1,1,'Squigglefin Crablord'),
(113388,121373,0,1.5400,0,1,0,1,1,'Squigglefin Crablord'),
(113388,140221,0,1.5100,0,1,0,1,1,'Squigglefin Crablord'),
(113388,121343,0,1.4400,0,1,0,1,1,'Squigglefin Crablord'),
(113388,121021,0,1.3900,0,1,0,1,1,'Squigglefin Crablord'),
(113388,121008,0,1.3700,0,1,0,1,1,'Squigglefin Crablord'),
(113388,121040,0,1.3700,0,1,0,1,1,'Squigglefin Crablord'),
(113388,121347,0,1.3700,0,1,0,1,1,'Squigglefin Crablord'),
(113388,121342,0,1.2600,0,1,0,1,1,'Squigglefin Crablord'),
(115054,132204,0,74.1600,0,1,0,1,1,'Wyrmtongue Scavenger'),
(115054,132199,0,21.5200,0,1,0,1,1,'Wyrmtongue Scavenger'),
(115054,124437,0,10.7300,0,1,0,1,1,'Wyrmtongue Scavenger'),
(115054,132200,0,3.6800,0,1,0,1,1,'Wyrmtongue Scavenger'),
(115431,132184,0,79.3300,0,1,0,1,1,'Savage Great White'),
(115431,132183,0,18.1800,0,1,0,1,1,'Savage Great White'),
(115431,124439,0,6.8400,0,1,0,1,1,'Savage Great White'),
(115431,132182,0,2.4900,0,1,0,1,1,'Savage Great White'),
(115434,132184,0,74.6600,0,1,0,1,1,'Isle Hammerhead'),
(115434,132183,0,13.4900,0,1,0,1,1,'Isle Hammerhead'),
(115434,132182,0,11.7600,0,1,0,1,1,'Isle Hammerhead'),
(115434,124439,0,6.0300,0,1,0,1,1,'Isle Hammerhead'),
(115601,132231,0,78.3300,0,1,0,1,1,'Infernal Hound'),
(115601,132199,0,16.5500,0,1,0,1,1,'Infernal Hound'),
(115601,132197,0,5.0300,0,1,0,1,1,'Infernal Hound'),
(115646,132204,0,73.2000,0,1,0,1,1,'Felfire Tormentor'),
(115646,132199,0,21.0900,0,1,0,1,1,'Felfire Tormentor'),
(115646,124437,0,8.7900,0,1,0,1,1,'Felfire Tormentor'),
(115646,132200,0,5.6200,0,1,0,1,1,'Felfire Tormentor'),
(115646,138781,0,1.1300,0,1,0,1,1,'Felfire Tormentor'),
(115648,132204,0,75.9500,0,1,0,1,1,'Azrok the Torturer'),
(115648,132199,0,21.6800,0,1,0,1,1,'Azrok the Torturer'),
(115648,124437,0,9.9300,0,1,0,1,1,'Azrok the Torturer'),
(115648,132200,0,2.3400,0,1,0,1,1,'Azrok the Torturer'),
(116427,124437,0,50.6500,0,1,0,1,1,'Felbound Spirit'),
(116427,121358,0,2.9200,0,1,0,1,1,'Felbound Spirit'),
(116466,124437,0,49.3400,0,1,0,1,1,'Felbound Spirit'),
(116466,121343,0,2.7400,0,1,0,1,1,'Felbound Spirit'),
(116466,121367,0,2.3400,0,1,0,1,1,'Felbound Spirit'),
(116466,121335,0,2.0000,0,1,0,1,1,'Felbound Spirit'),
(116466,121354,0,1.9100,0,1,0,1,1,'Felbound Spirit'),
(116466,121370,0,1.3200,0,1,0,1,1,'Felbound Spirit'),
(116466,121356,0,1.1000,0,1,0,1,1,'Felbound Spirit'),
(116466,138781,0,1.0500,0,1,0,1,1,'Felbound Spirit'),
(116468,124437,0,59.8700,0,1,0,1,1,'Felbound Spirit'),
(116468,140227,0,1.8200,0,1,0,1,1,'Felbound Spirit'),
(116468,121358,0,1.7800,0,1,0,1,1,'Felbound Spirit'),
(116468,138781,0,1.4800,0,1,0,1,1,'Felbound Spirit'),
(116468,121344,0,1.3600,0,1,0,1,1,'Felbound Spirit'),
(116468,121339,0,1.3200,0,1,0,1,1,'Felbound Spirit'),
(116469,124437,0,54.5000,0,1,0,1,1,'Felbound Spirit'),
(116469,121341,0,6.1700,0,1,0,1,1,'Felbound Spirit'),
(116469,121367,0,3.0400,0,1,0,1,1,'Felbound Spirit'),
(116469,121335,0,2.7900,0,1,0,1,1,'Felbound Spirit'),
(116469,121360,0,2.0700,0,1,0,1,1,'Felbound Spirit'),
(116469,144345,0,1.5500,0,1,0,1,1,'Felbound Spirit'),
(116469,121355,0,1.4000,0,1,0,1,1,'Felbound Spirit'),
(116469,140227,0,1.2500,0,1,0,1,1,'Felbound Spirit'),
(116469,138781,0,1.2300,0,1,0,1,1,'Felbound Spirit'),
(116470,124437,0,51.8200,0,1,0,1,1,'Felbound Spirit'),
(116470,121341,0,2.5900,0,1,0,1,1,'Felbound Spirit'),
(116470,121369,0,2.4200,0,1,0,1,1,'Felbound Spirit'),
(116470,121370,0,2.3800,0,1,0,1,1,'Felbound Spirit'),
(116470,121346,0,1.7800,0,1,0,1,1,'Felbound Spirit'),
(116470,121411,0,1.3700,0,1,0,1,1,'Felbound Spirit'),
(116470,138781,0,1.2900,0,1,0,1,1,'Felbound Spirit'),
(116472,124437,0,55.5900,0,1,0,1,1,'Felbound Spirit'),
(116472,140227,0,2.1500,0,1,0,1,1,'Felbound Spirit'),
(116472,121365,0,1.8200,0,1,0,1,1,'Felbound Spirit'),
(116472,121331,0,1.2900,0,1,0,1,1,'Felbound Spirit'),
(116472,138781,0,1.2500,0,1,0,1,1,'Felbound Spirit'),
(116472,121342,0,1.1900,0,1,0,1,1,'Felbound Spirit'),
(116472,121360,0,1.1300,0,1,0,1,1,'Felbound Spirit'),
(116472,140222,0,1.1100,0,1,0,1,1,'Felbound Spirit'),
(116472,121344,0,1.0000,0,1,0,1,1,'Felbound Spirit'),
(118264,124437,0,46.5100,0,1,0,1,1,'Felbound Spirit'),
(118264,121352,0,3.3800,0,1,0,1,1,'Felbound Spirit'),
(118264,121333,0,2.4900,0,1,0,1,1,'Felbound Spirit'),
(118264,140225,0,1.9900,0,1,0,1,1,'Felbound Spirit'),
(118264,121340,0,1.8300,0,1,0,1,1,'Felbound Spirit'),
(118264,121356,0,1.6400,0,1,0,1,1,'Felbound Spirit'),
(118264,121337,0,1.0700,0,1,0,1,1,'Felbound Spirit'),
(118264,121345,0,1.0000,0,1,0,1,1,'Felbound Spirit'),
(118294,124437,0,53.4200,0,1,0,1,1,'Felbound Spirit'),
(118294,147729,0,4.4700,0,1,0,1,1,'Felbound Spirit'),
(118294,121350,0,2.4100,0,1,0,1,1,'Felbound Spirit'),
(118294,121357,0,2.0900,0,1,0,1,1,'Felbound Spirit'),
(118294,121333,0,1.5000,0,1,0,1,1,'Felbound Spirit'),
(118657,132233,0,74.1100,0,1,0,1,1,'Tormented Wailer'),
(118657,132234,0,21.7300,0,1,0,1,1,'Tormented Wailer'),
(118657,132232,0,4.0600,0,1,0,1,1,'Tormented Wailer'),
(118660,132233,0,74.1900,0,1,0,1,1,'Broken Soul'),
(118660,132234,0,20.0700,0,1,0,1,1,'Broken Soul'),
(118660,124437,0,10.2100,0,1,0,1,1,'Broken Soul'),
(118660,132232,0,5.4100,0,1,0,1,1,'Broken Soul'),
(118679,124437,0,50.8200,0,1,0,1,1,'Deepsurge Tidesinger'),
(118679,138781,0,1.4000,0,1,0,1,1,'Deepsurge Tidesinger'),
(118679,121370,0,1.3800,0,1,0,1,1,'Deepsurge Tidesinger'),
(118679,121344,0,1.2900,0,1,0,1,1,'Deepsurge Tidesinger'),
(118679,121361,0,1.1800,0,1,0,1,1,'Deepsurge Tidesinger'),
(118680,124437,0,48.0000,0,1,0,1,1,'Deepsurge Punisher'),
(118680,121335,0,5.1500,0,1,0,1,1,'Deepsurge Punisher'),
(118680,121362,0,1.8200,0,1,0,1,1,'Deepsurge Punisher'),
(118680,138781,0,1.5600,0,1,0,1,1,'Deepsurge Punisher'),
(118680,121366,0,1.5000,0,1,0,1,1,'Deepsurge Punisher'),
(118683,124437,0,49.6300,0,1,0,1,1,'Deepsurge Defender'),
(118683,138781,0,1.2800,0,1,0,1,1,'Deepsurge Defender'),
(118683,121361,0,1.2400,0,1,0,1,1,'Deepsurge Defender'),
(118683,140227,0,1.1600,0,1,0,1,1,'Deepsurge Defender'),
(118943,124437,0,20.1000,0,1,0,1,1,'Felborne Abjurer'),
(119139,132184,0,72.2300,0,1,0,1,1,'Stoneblood Basilisk'),
(119139,132183,0,23.4300,0,1,0,1,1,'Stoneblood Basilisk'),
(119139,124439,0,7.5800,0,1,0,1,1,'Stoneblood Basilisk'),
(119139,132190,0,3.1100,0,1,0,1,1,'Stoneblood Basilisk'),
(120342,132204,0,69.9000,0,1,0,1,1,'Dread Felbat'),
(120342,132199,0,26.0400,0,1,0,1,1,'Dread Felbat'),
(120342,124437,0,11.1500,0,1,0,1,1,'Dread Felbat'),
(120342,132200,0,3.4500,0,1,0,1,1,'Dread Felbat');
UPDATE `creature_template` SET `lootid` = `entry` WHERE `entry` IN (98805,102106,106798,109702,110346,111454,111463,111632,111638,111674,111821,111823,113388,115054,115431,115434,115601,115646,115648,116427,116466,116468,116469,116470,116472,118264,118294,118657,118660,118679,118680,118683,118943,119139,120342);
@@ -0,0 +1,36 @@
-- Objets de quete manquants : Iles Brisees.
--
-- Trois objets sont des objectifs de quete reels chez nous (quest_objectives Type 1)
-- et n'existaient dans AUCUNE table de butin du serveur.
--
-- QuestRequired = 1 : le core ne fait tomber l'objet que pour un joueur ayant la quete.
--
-- Taux repris de la fiche PNJ de Wowhead, PAS de la fiche objet : les deux divergent
-- (Gemme pulsatile donnee a 19,66 % cote objet contre 82 % cote PNJ). C'est la fiche
-- PNJ qui est juste, verifiee a l'ecran : 879 421 sur 1 074 500 kills.
--
-- 44748, 46198 et 46236 sont des quetes MONDIALES (QuestType 3, QuestInfoID 109,
-- zone 7543 Rivage brise). Elles n'ont ni donneur ni rendeur par construction : leur
-- butin est desormais correct, mais leur disponibilite depend du systeme de quetes
-- mondiales, qui n'est pas verifie ici.
--
-- 41039 "Un savoir vole" NON TRAITEE. C'est pourtant la seule quete classique du lot,
-- avec donneur et rendeur presents, donc la seule reellement jouable et bloquee. Mais
-- son objet 133995 "Recette de biere de tempete" n'existe pas dans nos donnees : le
-- serveur rejette la ligne au chargement, avec
-- Table 'creature_loot_template' Entry 102852 Item 133995: item entry not listed
-- in `item_template` - skipped
-- L'objet figure dans Item.db2 mais pas dans ItemSparse.db2, et c'est ItemSparse qui
-- compte pour le serveur. Debloquer cette quete demande un enregistrement DB2 custom
-- par hotfix, pas une ligne de butin.
--
-- Non traite egalement : 147430, objectif de la quete 46765. Wowhead le donne sur
-- environ 200 PNJ de tout le jeu, de 0,01 a 100 pour cent : c'est un butin mondial,
-- qui releve d'une table de reference et non d'une ligne par creature.
DELETE FROM `creature_loot_template` WHERE (`Entry`,`Item`) IN ((115054,142079),(118943,144362),(119139,147396));
INSERT INTO `creature_loot_template`
(`Entry`,`Item`,`Reference`,`Chance`,`QuestRequired`,`LootMode`,`GroupId`,`MinCount`,`MaxCount`,`Comment`) VALUES
(115054,142079,0,24.8100,1,1,0,1,1,'Wyrmtongue Scavenger - Ravitaillement naufrage (quete mondiale 44748)'),
(118943,144362,0,81.8600,1,1,0,1,1,'Felborne Abjurer - Gemme pulsatile (quete mondiale 46198)'),
(119139,147396,0,61.9000,1,1,0,1,1,'Stoneblood Basilisk - Oeil de basilic (quete mondiale 46236)');
@@ -0,0 +1,9 @@
-- Butin de Morjirn (102852) : Recette du breuvage des tempetes, objectif de la quete
-- 41039 "Un savoir vole". Ne peut fonctionner qu'apres le hotfix DB2 creant l'objet
-- 133995 (voir sql/updates/hotfixes/2026_09_07_00) : sans lui le core rejette la ligne.
-- Taux 99,95 % releve sur la fiche PNJ de Wowhead, QuestRequired = 1.
DELETE FROM `creature_loot_template` WHERE `Entry` = 102852 AND `Item` = 133995;
INSERT INTO `creature_loot_template`
(`Entry`,`Item`,`Reference`,`Chance`,`QuestRequired`,`LootMode`,`GroupId`,`MinCount`,`MaxCount`,`Comment`) VALUES
(102852,133995,0,99.9500,1,1,0,1,1,'Morjirn - Recette du breuvage des tempetes (quete 41039)');
UPDATE `creature_template` SET `lootid` = `entry` WHERE `entry` = 102852;
@@ -0,0 +1,75 @@
-- Quete 46765 "Le rivage Brise : enquete sur la Legion" : objet 147430 introuvable.
--
-- Quete classique (QuestType 2, donneur present), donc reellement jouable, mais son
-- objectif n'existait dans aucune table de butin. Wowhead liste 199 lacheurs sur tout
-- le jeu : 199 existent dans notre creature_template, mais seulement 58 sont spawnes
-- chez nous. Sur ces 58, 49 lachent effectivement l'objet.
--
-- Taux releves une par une sur les fiches PNJ (pas la fiche objet), avec controle du
-- statut HTTP 200 et du marqueur g_npcs. Ils vont de 0,03 a 100 pour cent : les taux
-- tres bas sont conformes au releve, et l'abondance de sources compense.
-- QuestRequired = 1 : l'objet ne tombe que pour un joueur ayant la quete.
DELETE FROM `creature_loot_template` WHERE `Item` = 147430;
INSERT INTO `creature_loot_template`
(`Entry`,`Item`,`Reference`,`Chance`,`QuestRequired`,`LootMode`,`GroupId`,`MinCount`,`MaxCount`,`Comment`) VALUES
(91535,147430,0,7.6900,1,1,0,1,1,'Quartermaster Ricard'),
(91590,147430,0,100.0000,1,1,0,1,1,'Apothecary Withers'),
(91693,147430,0,12.5000,1,1,0,1,1,'Black Rose Apothecary'),
(92333,147430,0,25.0000,1,1,0,1,1,'Grue cendrecime'),
(92792,147430,0,33.3300,1,1,0,1,1,'Nightmare Rider'),
(95958,147430,0,20.0000,1,1,0,1,1,'Floating Treasure'),
(98112,147430,0,16.6700,1,1,0,1,1,'Steward Dayton'),
(100054,147430,0,10.0000,1,1,0,1,1,'Feltotem Warmonger'),
(100055,147430,0,9.0900,1,1,0,1,1,'Feltotem Bloodsinger'),
(101390,147430,0,1.2700,1,1,0,1,1,'Arch-Desecrator Malithar'),
(102660,147430,0,11.1100,1,1,0,1,1,'Jandvik Metalsmith'),
(102741,147430,0,1.2200,1,1,0,1,1,'Highcliff Gull'),
(104845,147430,0,1.1900,1,1,0,1,1,'Caged Macaw'),
(105645,147430,0,2.5600,1,1,0,1,1,'Penned Turtle'),
(105646,147430,0,8.3300,1,1,0,1,1,'Captive Basilisk'),
(105650,147430,0,2.5600,1,1,0,1,1,'Confined Raptor'),
(105687,147430,0,100.0000,1,1,0,1,1,'Valkyra Shieldmaiden'),
(106798,147430,0,1.0400,1,1,0,1,1,'Nora Blackfire'),
(108027,147430,0,1.1100,1,1,0,1,1,'Webmistress Shinaris'),
(108033,147430,0,2.3800,1,1,0,1,1,'Fal''dorei Web Walker'),
(108034,147430,0,2.3800,1,1,0,1,1,'Fal''dorei Reaver'),
(110354,147430,0,0.8400,1,1,0,1,1,'Coryn'),
(110839,147430,0,1.1600,1,1,0,1,1,'Stormwing Drake'),
(111643,147430,0,6.2500,1,1,0,1,1,'Cove Seagull'),
(112090,147430,0,16.6700,1,1,0,1,1,'Shoalfin Warrior'),
(115054,147430,0,0.1900,1,1,0,1,1,'Wyrmtongue Scavenger'),
(115056,147430,0,0.1300,1,1,0,1,1,'Felwing Devourer'),
(117088,147430,0,0.0500,1,1,0,1,1,'Arachniarch Bybee'),
(117093,147430,0,0.9000,1,1,0,1,1,'Felbringer Xar''thok'),
(117094,147430,0,1.0800,1,1,0,1,1,'Malorus the Soulkeeper'),
(117096,147430,0,1.2300,1,1,0,1,1,'Potionmaster Gloop'),
(117136,147430,0,1.4200,1,1,0,1,1,'Doombringer Zar''thoz'),
(117289,147430,0,0.0300,1,1,0,1,1,'Felblade Sentry'),
(117559,147430,0,0.5400,1,1,0,1,1,'Wrathguard Dreadblade'),
(118322,147430,0,0.2800,1,1,0,1,1,'Felborne Punisher'),
(118390,147430,0,0.3200,1,1,0,1,1,'Toiling Collector'),
(118971,147430,0,0.8300,1,1,0,1,1,'Felfin Terrorscale'),
(119139,147430,0,0.2100,1,1,0,1,1,'Stoneblood Basilisk'),
(119634,147430,0,0.0600,1,1,0,1,1,'Restless Remains'),
(119788,147430,0,0.1500,1,1,0,1,1,'Ravenous Carrionstalker'),
(120323,147430,0,0.1600,1,1,0,1,1,'Weary Lion Seal'),
(120342,147430,0,0.5900,1,1,0,1,1,'Dread Felbat'),
(120386,147430,0,5.8200,1,1,0,1,1,'Scavenging Crow'),
(120820,147430,0,0.1100,1,1,0,1,1,'Fixated Corruptor'),
(120896,147430,0,0.0500,1,1,0,1,1,'Dark Tormentor'),
(121034,147430,0,0.1100,1,1,0,1,1,'Ravenous Felstalker'),
(121035,147430,0,0.0600,1,1,0,1,1,'Felblade Sentry'),
(121058,147430,0,0.0700,1,1,0,1,1,'Wrathguard Soulflayer'),
(121346,147430,0,0.3000,1,1,0,1,1,'Dreadwing Terror');
-- Les lacheurs qui n'avaient encore aucune table doivent pointer sur elle.
UPDATE `creature_template` SET `lootid` = `entry` WHERE `entry` IN (91535,91590,91693,92333,92792,95958,98112,100054,100055,101390,102660,102741,104845,105645,105646,105650,105687,106798,108027,108033,108034,110354,110839,111643,112090,115054,115056,117088,117093,117094,117096,117136,117289,117559,118322,118390,118971,119139,119634,119788,120323,120342,120386,120820,120896,121034,121035,121058,121346) AND `lootid` = 0;
-- 100231 : seul objet valide de son releve, Meteorite gangrenee (147869), objet de
-- pacotille. Il etait ecarte par mon filtre "deja utilise ailleurs", trop prudent ici.
DELETE FROM `creature_loot_template` WHERE `Entry` = 100231 AND `Item` = 147869;
INSERT INTO `creature_loot_template`
(`Entry`,`Item`,`Reference`,`Chance`,`QuestRequired`,`LootMode`,`GroupId`,`MinCount`,`MaxCount`,`Comment`) VALUES
(100231,147869,0,5.8800,0,1,0,1,1,'Meteorite gangrenee');
UPDATE `creature_template` SET `lootid` = `entry` WHERE `entry` = 100231 AND `lootid` = 0;
@@ -0,0 +1,16 @@
-- 113198 "Trapped Thicket Hunter" : bete des Iles Brisees sans table de depecage.
--
-- Son releve Wowhead ne donnait que des materiaux de depecage (124113 Cuir de rochepeau
-- a 100 %, plus trois objets a 12,5 %). Ces objets relevent de skinning_loot_template,
-- pas de creature_loot_template : chez nous 124113 apparait 284 fois en depecage contre
-- 19 fois en butin de cadavre.
--
-- Les 12,5 % correspondent a 1 observation sur 8 : statistiquement sans valeur, ecartes.
-- Le taux retenu pour le Cuir de rochepeau est celui d'une bete Legion comparable deja
-- configuree chez nous (90134 Llothien Grizzly, 99,6779 %, 1 a 2 unites) plutot qu'un
-- 100 % tire d'un echantillon de huit.
DELETE FROM `skinning_loot_template` WHERE `Entry` = 113198;
INSERT INTO `skinning_loot_template`
(`Entry`,`Item`,`Reference`,`Chance`,`QuestRequired`,`LootMode`,`GroupId`,`MinCount`,`MaxCount`,`Comment`) VALUES
(113198,124113,0,99.6779,0,1,0,1,2,'Trapped Thicket Hunter - Cuir de rochepeau');
UPDATE `creature_template` SET `skinloot` = `entry` WHERE `entry` = 113198;
@@ -0,0 +1,24 @@
-- 142 lignes de butin rejetees a chaque chargement : REPARATION, pas suppression.
--
-- Les supprimer aurait ete destructeur : ce ne sont pas des lignes vides mais du butin
-- reel rendu inoperant par deux defauts d'import distincts.
--
-- 1) 62 lignes a chance NEGATIVE, sur 35 creatures. Signe inverse a l'import : les memes
-- objets existent ailleurs dans la table avec la meme valeur en positif (69815 entre
-- 50 et 100 %, 71141 entre 75 et 100 %). On remet la valeur absolue.
--
-- 2) 80 lignes a chance nulle sans GroupId, sur 6 boss de points d'invasion majeurs
-- d'Argus (Sotanathor, Inquisitor Meto, Mistress Alluradel, Pit Lord Vilemus,
-- Matron Folnuna, Occularus), 12 a 14 objets chacun. C'est l'encodage classique d'un
-- groupe equiprobable dont le GroupId a ete perdu : sans lui le core rejette tout et
-- ces boss ne lachent rien. Verifie sur Wowhead : Mistress Alluradel liste exactement
-- les 14 memes objets que nous. GroupId = 1 les fait tirer un objet parmi le lot.
--
-- Effet : 41 creatures retrouvent leur butin, dont 6 boss d'Argus qui n'en avaient aucun.
UPDATE `creature_loot_template` SET `Chance` = ABS(`Chance`)
WHERE `Reference` = 0 AND `Chance` <> 0 AND `Chance` < 0.000001;
UPDATE `creature_loot_template` SET `GroupId` = 1
WHERE `Reference` = 0 AND `Chance` = 0 AND `GroupId` = 0
AND `Entry` IN (124492,124514,124555,124592,124625,124719);
@@ -0,0 +1,90 @@
-- Depecage manquant : betes des Iles Brisees (carte 1220).
--
-- 81 betes spawnees avaient skinloot = 0. Relevees sur Wowhead (onglet 'skinning',
-- distinct de l'onglet des butins), avec controle du statut HTTP 200 et du marqueur
-- g_npcs. Aucun refus rencontre.
--
-- 67 n'ont AUCUN onglet depecage sur Wowhead : oiseaux, insectes, gasteropodes,
-- araignees, tortues en cage. Verifie a la main sur cinq d'entre elles (Suramar
-- Skyhunter, Gritslime Snail, Ruins Recluse, Cove Seagull, Penned Turtle) : l'onglet
-- est reellement absent, ce n'est pas un defaut d'extraction. Elles ne sont pas touchees.
--
-- Methode validee avant collecte sur un temoin dont la table existe deja chez nous
-- (90134 Llothien Grizzly) : les 8 memes objets, memes intervalles de pile, ecarts de
-- taux sous le bruit statistique. Nos donnees existantes viennent de la meme source.
--
-- Attention, l'onglet 'skinning' de Wowhead n'est PAS du JSON strict comme celui des
-- butins : les cles y sont non quotees (count:148807,stack:[1,2]) et il y a une espace
-- avant outof. JSON.parse echoue, et un motif trop strict renvoie zero partout.
DELETE FROM `skinning_loot_template` WHERE `Entry` IN (89013,89014,89016,89385,89386,89391,89652,89653,89696,89891,97928,98356,111380,113408);
INSERT INTO `skinning_loot_template`
(`Entry`,`Item`,`Reference`,`Chance`,`QuestRequired`,`LootMode`,`GroupId`,`MinCount`,`MaxCount`,`Comment`) VALUES
(89013,124113,0,99.5800,0,1,0,1,2,'Azsuna Lion Seal'),
(89013,124124,0,1.6500,0,1,0,1,1,'Azsuna Lion Seal'),
(89013,124439,0,13.0700,0,1,0,2,8,'Azsuna Lion Seal'),
(89013,129746,0,1.7000,0,1,0,1,1,'Azsuna Lion Seal'),
(89013,129888,0,1.7800,0,1,0,1,1,'Azsuna Lion Seal'),
(89013,129906,0,17.8800,0,1,0,1,1,'Azsuna Lion Seal'),
(89013,134807,0,15.8800,0,1,0,1,1,'Azsuna Lion Seal'),
(89014,124113,0,99.9400,0,1,0,1,2,'Grassland Heron'),
(89014,124438,0,17.3400,0,1,0,3,8,'Grassland Heron'),
(89014,129888,0,35.5800,0,1,0,1,1,'Grassland Heron'),
(89014,139894,0,2.1800,0,1,0,1,1,'Grassland Heron'),
(89016,124113,0,99.4900,0,1,0,1,2,'Ravyn-Drath'),
(89016,124439,0,16.4600,0,1,0,2,8,'Ravyn-Drath'),
(89016,129860,0,2.7000,0,1,0,1,1,'Ravyn-Drath'),
(89016,129888,0,2.8300,0,1,0,1,1,'Ravyn-Drath'),
(89016,129906,0,15.9400,0,1,0,1,1,'Ravyn-Drath'),
(89385,124113,0,100.0000,0,1,0,1,2,'Azsuna Fox'),
(89385,124124,0,0.8100,0,1,0,1,1,'Azsuna Fox'),
(89385,124438,0,13.4100,0,1,0,3,8,'Azsuna Fox'),
(89385,124439,0,12.2400,0,1,0,3,8,'Azsuna Fox'),
(89385,129746,0,1.1800,0,1,0,1,1,'Azsuna Fox'),
(89385,129888,0,8.0600,0,1,0,1,1,'Azsuna Fox'),
(89386,124115,0,99.2400,0,1,0,1,3,'Cliffwing Hippogryph'),
(89386,124124,0,1.3500,0,1,0,1,1,'Cliffwing Hippogryph'),
(89386,124438,0,16.6800,0,1,0,3,8,'Cliffwing Hippogryph'),
(89386,129746,0,1.5200,0,1,0,1,1,'Cliffwing Hippogryph'),
(89386,129894,0,4.0300,0,1,0,1,1,'Cliffwing Hippogryph'),
(89386,129908,0,55.0500,0,1,0,1,1,'Cliffwing Hippogryph'),
(89386,134806,0,19.0100,0,1,0,1,1,'Cliffwing Hippogryph'),
(89391,124113,0,100.0000,0,1,0,1,1,'Cursefeather Owl'),
(89391,124438,0,4.5900,0,1,0,3,8,'Cursefeather Owl'),
(89391,129888,0,8.1800,0,1,0,1,1,'Cursefeather Owl'),
(89652,124113,0,100.0000,0,1,0,1,2,'Shallows Heron'),
(89652,124438,0,6.5600,0,1,0,3,8,'Shallows Heron'),
(89652,129860,0,1.5900,0,1,0,1,1,'Shallows Heron'),
(89652,129888,0,18.0300,0,1,0,1,1,'Shallows Heron'),
(89652,139894,0,0.7300,0,1,0,1,1,'Shallows Heron'),
(89653,124115,0,99.9900,0,1,0,1,3,'Gangamesh'),
(89653,124438,0,10.3400,0,1,0,2,8,'Gangamesh'),
(89653,124439,0,7.0700,0,1,0,1,8,'Gangamesh'),
(89653,129862,0,8.3100,0,1,0,1,1,'Gangamesh'),
(89653,129894,0,12.6400,0,1,0,1,1,'Gangamesh'),
(89696,124115,0,99.9700,0,1,0,1,3,'Horned Leatherback'),
(89696,124438,0,13.1400,0,1,0,1,8,'Horned Leatherback'),
(89696,129862,0,1.0100,0,1,0,1,1,'Horned Leatherback'),
(89696,129894,0,25.9800,0,1,0,1,1,'Horned Leatherback'),
(89891,124115,0,99.9800,0,1,0,1,3,'Dragon Turtle'),
(89891,124438,0,9.8200,0,1,0,3,8,'Dragon Turtle'),
(89891,129746,0,1.3500,0,1,0,1,1,'Dragon Turtle'),
(89891,129862,0,2.3200,0,1,0,1,1,'Dragon Turtle'),
(89891,129865,0,5.2200,0,1,0,1,1,'Dragon Turtle'),
(89891,129894,0,4.5600,0,1,0,1,1,'Dragon Turtle'),
(89891,139894,0,0.7700,0,1,0,1,1,'Dragon Turtle'),
(97928,124115,0,99.9500,0,1,0,1,1,'Tamed Coralback'),
(97928,124438,0,13.2100,0,1,0,3,8,'Tamed Coralback'),
(97928,129862,0,3.5900,0,1,0,1,1,'Tamed Coralback'),
(97928,129894,0,15.6200,0,1,0,1,1,'Tamed Coralback'),
(98356,124113,0,100.0000,0,1,0,1,1,'Corrupted Great Eagle'),
(98356,124438,0,4.5500,0,1,0,5,7,'Corrupted Great Eagle'),
(98356,129746,0,2.2700,0,1,0,1,1,'Corrupted Great Eagle'),
(98356,129888,0,2.2700,0,1,0,1,1,'Corrupted Great Eagle'),
(111380,124115,0,100.0000,0,1,0,1,1,'Killer Orca'),
(111380,124439,0,72.5500,0,1,0,3,6,'Killer Orca'),
(111380,129862,0,45.1000,0,1,0,1,1,'Killer Orca'),
(111380,129894,0,1.9600,0,1,0,1,1,'Killer Orca'),
(113408,124113,0,100.0000,0,1,0,1,1,'Salty');
UPDATE `creature_template` SET `skinloot` = `entry` WHERE `entry` IN (89013,89014,89016,89385,89386,89391,89652,89653,89696,89891,97928,98356,111380,113408);
@@ -0,0 +1,15 @@
-- Archmage Khadgar (78288) : faction hostile a l'Alliance, a tort.
--
-- Signalement : un personnage Alliance debarque a Crete-de-Gel et se fait attaquer par
-- trois PNJ. Deux sont conformes : Thrall (70859) et Farseer Drek'Thar (76411) sont des
-- chefs de la Horde, hostiles a l'Alliance sur les royaumes officiels comme chez nous.
--
-- Le troisieme non. Khadgar est neutre dans tout Draenor : il sert les deux factions.
-- Wowhead le confirme pour cette entree precise (react Alliance = amical, Horde = amical),
-- et notre propre base aussi : sur les 150 entrees "Archmage Khadgar", 100 sont en
-- faction 35, et 78288 est la SEULE en faction 83 (Orgrimmar).
--
-- Il est spawne deux fois, toutes deux en zone 6720 : au point de debarquement (7257)
-- et pres du fief (7004). Un joueur Alliance qui passe par la se fait donc agresser par
-- un PNJ cense etre neutre.
UPDATE `creature_template` SET `faction` = 35 WHERE `entry` = 78288;
@@ -0,0 +1,9 @@
-- Ken-Ken (60979) : faction 14, donc hostile a TOUT LE MONDE, alors qu'il porte le
-- drapeau de dialogue (npcflag 1). Personne ne pouvait lui parler.
--
-- Meme classe d'erreur que Khadgar 78288, trouvee par le meme signal : sur les 8 entrees
-- "Ken-Ken" de la base, six sont en faction 35 et celle-ci est la seule en 14.
-- Wowhead le donne amical envers l'Alliance ET la Horde.
--
-- Deux spawns concernes, foret de Jade (carte 870, zone 5841).
UPDATE `creature_template` SET `faction` = 35 WHERE `entry` = 60979;
+38 -12
View File
@@ -502,9 +502,30 @@ Position BotUtility::GetPositionFromGroup(Player* pCenterPlayer, ObjectGuid self
float distZ = centerPos.GetPositionZ();
if (!pCenterPlayer->IsFlying())
distZ = pCenterPlayer->GetMap()->GetHeight(pCenterPlayer->GetPhaseShift(), distX, distY, distZ);
// =================================================================
// SylvaniaCore : la position calculee etait JETEE.
//
// SIGNALE EN JEU : « les bots restent les uns sur les autres ».
//
// Tout le calcul ci-dessus est juste -- un angle propre a chaque
// membre, reparti sur le cercle, a quatre metres du meneur. Mais le
// resultat n'etait jamais renvoye :
//
// Position resultPos(distX, distY, distZ, ...); // calculee
// Position pos; // vide
// pCenterPlayer->GetFirstCollisionPosition(...); // jete
// return pos; // (0,0,0)
//
// GetFirstCollisionPosition REND une Position ; son resultat etait
// ignore, et l'on renvoyait une variable jamais renseignee. La
// fonction rendait donc l'origine du monde a tous les appelants, et
// la formation n'a jamais fonctionne -- nulle part, pas seulement
// dans ce scenario.
// =================================================================
Position resultPos(distX, distY, distZ, pCenterPlayer->GetOrientation());
Position pos;
pCenterPlayer->GetFirstCollisionPosition(pCenterPlayer->GetDistance(resultPos), pCenterPlayer->GetRelativeAngle(&resultPos));
Position pos = pCenterPlayer->GetFirstCollisionPosition(
pCenterPlayer->GetDistance(resultPos), pCenterPlayer->GetRelativeAngle(&resultPos));
pos.SetOrientation(pCenterPlayer->GetOrientation());
return pos;
}
@@ -1031,16 +1052,21 @@ void BotAITeleport::Update(uint32 diff, BotBGAIMovement* pMovement)
}
else if (m_TeleportStep == 3)
{
WorldSession* pSession = me->GetSession();
MovementInfo movementInfo;
movementInfo.pos = m_TeleportPositon;
movementInfo.time = getMSTime();
movementInfo.guid = me->GetGUID();
movementInfo.flags = 0;
movementInfo.flags2 = 0;
WorldPacket data(CMSG_MOVE_FALL_LAND);// MSG_MOVE_STOP);
data << movementInfo;
me->SendMessageToSet(&data, me);
// =====================================================
// SylvaniaCore : envoi mort retire.
//
// Le bloc supprime ici diffusait un paquet portant
// l'opcode CLIENT CMSG_MOVE_FALL_LAND. WorldSession le
// REFUSE systematiquement -- « Prevented sending of
// opcode 14841 with non existing handler » -- et ecrit
// une ligne d'ERREUR a chaque tentative. Signale par un
// utilisateur de SylvaniaCore : des milliers de lignes
// par match de champ de bataille.
//
// Il n'accomplissait donc rien. La reconstruction de
// l'objet ci-dessous, ajoutee precedemment pour pallier
// son inefficacite, reste seule en charge du travail.
// =====================================================
me->CombatStop(true);
me->SetSelection(ObjectGuid::Empty);
// SylvaniaCore : le paquet diffuse ci-dessus porte un opcode CLIENT
+22 -13
View File
@@ -674,19 +674,28 @@ void BotBGAIMovement::SyncPosition(Position& pos, bool immed)
if (m_Player->IsFlying())
return;
Position targetPos(pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ(), m_Player->GetOrientation());
targetPos.m_positionZ = 0.01f + m_Player->GetMap()->GetHeight(m_Player->GetPhaseShift(), targetPos.GetPositionX(), targetPos.GetPositionY(), targetPos.m_positionZ);
WorldSession* pSession = m_Player->GetSession();
MovementInfo movementInfo;
movementInfo.pos = targetPos;
movementInfo.time = getMSTime();
movementInfo.guid = m_Player->GetGUID();
movementInfo.flags = 0;
movementInfo.flags2 = 0;
WorldPacket data(CMSG_MOVE_FALL_LAND);// MSG_MOVE_STOP);
data << movementInfo;
m_Player->SendMessageToSet(&data, m_Player);
// =================================================================
// SylvaniaCore : cette fonction etait entierement inerte.
//
// Elle calculait une position au sol puis diffusait un paquet
// portant l'opcode CLIENT CMSG_MOVE_FALL_LAND. WorldSession le
// REFUSE toujours -- « Prevented sending of opcode 14841 with non
// existing handler » -- et journalise une ERREUR a chaque fois.
// Signale par un utilisateur de SylvaniaCore : des milliers de
// lignes par match, avec des bots en champ de bataille.
//
// Elle ne deplacait meme pas le bot cote serveur : aucun appel a
// UpdatePosition. Son seul effet observable etait donc le journal.
//
// L'envoi est retire. La fonction ne fait desormais plus que sa
// temporisation, ce qui est exactement ce qu'elle accomplissait
// avant -- en silence cette fois.
//
// A REPRENDRE : si une desynchronisation des bots est constatee en
// jeu, c'est ICI qu'il faut la corriger, par un UpdatePosition en
// bonne et due forme. Rien n'a ete ajoute a l'aveugle : le defaut
// signale ne pouvait pas etre reproduit sur notre serveur.
// =================================================================
}
bool BotBGAIMovement::IsNearToPosition(float x, float y, float z, float range)
@@ -264,9 +264,22 @@ void BotGroupAI::ProcessAttackCommand()
{
Unit* pMasterTarget = m_MasterPlayer->GetSelectedUnit();
if (!pMasterTarget || !pMasterTarget->IsAlive())
{
TC_LOG_ERROR("botai", "ATTACKDBG %s: ordre recu mais le maitre n a pas de cible vivante.",
me->GetName().c_str());
return;
}
if (!me->IsValidAttackTarget(pMasterTarget))
{
TC_LOG_ERROR("botai", "ATTACKDBG %s: cible %u (entree %u) refusee par IsValidAttackTarget.",
me->GetName().c_str(), pMasterTarget->GetGUID().GetCounter(),
pMasterTarget->ToCreature() ? pMasterTarget->ToCreature()->GetEntry() : 0);
return;
}
TC_LOG_ERROR("botai", "ATTACKDBG %s: cible %u (entree %u) retenue, distance %.1f.",
me->GetName().c_str(), pMasterTarget->GetGUID().GetCounter(),
pMasterTarget->ToCreature() ? pMasterTarget->ToCreature()->GetEntry() : 0,
me->GetDistance(pMasterTarget));
me->SetSelection(pMasterTarget->GetGUID());
m_ForceFlee = false;
m_StopFollow = false;
@@ -1448,7 +1461,26 @@ bool BotGroupAI::TryTeleportToMaster()
m_Flee.Clear();
return true;
}
else if (me->GetDistance(m_MasterPlayer->GetPosition()) > BOTAI_SEARCH_RANGE * 5)
// DISTANCE_DE_RAPPEL
//
// MESURE (audit du 09/09) : au moment d un « !attack », deux des quatre
// mercenaires se trouvaient a 100 et 106 metres de leur employeur, les
// deux autres a 21 et 28. Les lointains n etaient pas perdus au sens du
// code -- simplement sous le seuil de rappel, qui valait
// BOTAI_SEARCH_RANGE * 5, soit 160 metres.
//
// Ils recevaient donc l ordre, l acceptaient, et partaient A PIED. Cent
// metres de marche, c est une quinzaine de secondes : le combat est
// termine avant leur arrivee, et de l exterieur le mercenaire a
// simplement « refuse d attaquer ».
//
// Ce seuil de 160 convient a un bot de terrain qui vagabonde ; pas a un
// membre de groupe, qui n a aucune raison de s eloigner autant de son
// employeur. On le ramene a deux fois la portee de recherche, soit 64
// metres -- largement au-dela des 32 metres ou l IA engage le combat et
// des 28 de portee des sorts, donc sans risque de teleporter un lanceur
// de sorts en pleine rotation.
else if (me->GetDistance(m_MasterPlayer->GetPosition()) > BOTAI_SEARCH_RANGE * 2)
{
m_Teleporting.SetTeleport(m_MasterPlayer, 0);
me->SetSelection(ObjectGuid::Empty);
@@ -1528,18 +1560,44 @@ void BotGroupAI::ProcessFollowToMaster()
}
Position targetPos = BotUtility::GetPositionFromGroup(m_MasterPlayer, me->GetGUID(), me->GetGroup());
m_Movement->MovementTo(targetPos.GetPositionX(), targetPos.GetPositionY(), targetPos.GetPositionZ(), 0);
// =================================================================
// ANGLE PROPRE A CHAQUE MEMBRE.
//
// Les deux MoveFollow ci-dessous employaient la MEME distance et le
// MEME angle pour tout le monde -- un metre devant le meneur, a son
// orientation. Tous les bots convergeaient donc vers un point unique
// et se superposaient, quand bien meme GetPositionFromGroup leur
// avait calcule une place distincte deux lignes plus haut.
//
// On reprend l'angle de cette formation : chacun garde son secteur.
float suiviAngle = 0.0f;
float suiviDistance = 3.0f;
{
uint32 index = 0;
uint32 count = 0;
ObjectGuid monGuid = me->GetGUID();
if (me->GetGroup() && me->GetGroup()->GiveAtGroupPos(monGuid, index, count))
{
if (count > 1)
--count;
if (count)
suiviAngle = (float(M_PI) * 2.0f / float(count)) * float(index);
suiviDistance = (count > 3) ? 4.0f : 3.0f;
}
}
float distance = me->GetDistance(m_MasterPlayer->GetPosition());
if (distance <= NEEDFLEE_CHECKRANGE && distance > 0.1f)
{
me->GetMotionMaster()->Clear();
me->GetMotionMaster()->MoveFollow(m_MasterPlayer, 1.0f, m_MasterPlayer->GetOrientation());
me->GetMotionMaster()->MoveFollow(m_MasterPlayer, suiviDistance, suiviAngle);
return;
}
if (me->IsWithinLOSInMap(m_MasterPlayer))
{
me->GetMotionMaster()->Clear();
me->GetMotionMaster()->MoveFollow(m_MasterPlayer, 1.0f, m_MasterPlayer->GetOrientation());
me->GetMotionMaster()->MoveFollow(m_MasterPlayer, suiviDistance, suiviAngle);
}
else
m_Movement->MovementTo(m_MasterPlayer->GetPositionX(), m_MasterPlayer->GetPositionY(), m_MasterPlayer->GetPositionZ(), 1);
@@ -2980,6 +3038,20 @@ Unit* BotGroupAI::GetBotAIValidSelectedUnit()
isValid = false;
if (!isValid)
{
// SONDE ATTACKDBG : nommer le filtre qui rejette, une fois par
// seconde au plus pour ne pas noyer le journal.
if (pTarget && m_AttackDbgTick + 1000 < getMSTime())
{
m_AttackDbgTick = getMSTime();
char const* motif = "inconnu";
if (!pTarget->IsVisible()) motif = "IsVisible";
else if (!me->InSamePhase(pTarget->GetPhaseShift())) motif = "phase";
else if (IsNotSelect(pTarget)) motif = "IsNotSelect";
else if (TargetIsControl(pTarget)) motif = "controle/evade";
else if (m_FliterCreatures.IsFliterCreature(pTarget->ToCreature())) motif = "filtre";
TC_LOG_ERROR("botai", "ATTACKDBG %s: cible %u rejetee -- %s.",
me->GetName().c_str(), pTarget->GetGUID().GetCounter(), motif);
}
me->AttackStop();
me->SetSelection(ObjectGuid::Empty);
return NULL;
@@ -498,6 +498,9 @@ void CriteriaHandler::UpdateCriteria(CriteriaTypes type, uint64 miscValue1 /*= 0
case CRITERIA_TYPE_ON_LOGIN:
case CRITERIA_TYPE_PLACE_GARRISON_BUILDING:
case CRITERIA_TYPE_COLLECT_BATTLEPET:
case CRITERIA_TYPE_CAPTURE_PET_IN_BATTLE:
case CRITERIA_TYPE_BATTLEPET_WIN:
case CRITERIA_TYPE_BATTLEPET_LEVEL_UP:
case CRITERIA_TYPE_HONOR_LEVEL_REACHED:
case CRITERIA_TYPE_BUY_GUILD_TABARD:
case CRITERIA_TYPE_PRESTIGE_REACHED:
@@ -759,9 +762,6 @@ void CriteriaHandler::UpdateCriteria(CriteriaTypes type, uint64 miscValue1 /*= 0
case CRITERIA_TYPE_BE_KICKED_FROM_LFR:
case CRITERIA_TYPE_COUNT_OF_LFR_QUEUE_BOOSTS_BY_TANK:
case CRITERIA_TYPE_COMPLETE_SCENARIO_COUNT:
case CRITERIA_TYPE_CAPTURE_PET_IN_BATTLE:
case CRITERIA_TYPE_BATTLEPET_WIN:
case CRITERIA_TYPE_BATTLEPET_LEVEL_UP:
case CRITERIA_TYPE_CAPTURE_BATTLE_PET_CREDIT:
case CRITERIA_TYPE_ENTER_AREA:
case CRITERIA_TYPE_LEAVE_AREA:
@@ -1207,6 +1207,9 @@ bool CriteriaHandler::IsCompletedCriteria(Criteria const* criteria, uint64 requi
case CRITERIA_TYPE_SEND_EVENT_SCENARIO:
case CRITERIA_TYPE_COMPLETE_SCENARIO:
case CRITERIA_TYPE_COLLECT_BATTLEPET:
case CRITERIA_TYPE_CAPTURE_PET_IN_BATTLE:
case CRITERIA_TYPE_BATTLEPET_WIN:
case CRITERIA_TYPE_BATTLEPET_LEVEL_UP:
return progress->Counter >= requiredAmount;
case CRITERIA_TYPE_CAPTURE_SPECIFIC_BATTLEPET:
case CRITERIA_TYPE_COMPLETE_ACHIEVEMENT:
@@ -1622,6 +1625,12 @@ bool CriteriaHandler::RequirementsSatisfied(Criteria const* criteria, uint64 mis
if (!miscValue2 /*login case*/ || miscValue1 != uint32(criteria->Entry->Asset.EquipmentSlot))
return false;
break;
case CRITERIA_TYPE_BATTLEPET_LEVEL_UP:
// miscValue1 = espece de la mascotte, miscValue2 = niveau atteint,
// Asset.ID = niveau exige par le critere
if (!miscValue1 || !miscValue2 || miscValue2 != uint64(criteria->Entry->Asset.ID))
return false;
break;
default:
break;
}
+2 -2
View File
@@ -1288,7 +1288,7 @@ void PetBattle::Finish(uint32 winnerTeamID, bool aborted, bool ignoreAbandonPena
if (auto speciesInfo = sBattlePetSpeciesStore.LookupEntry(currentPet->Species))
{
player->UpdateCriteria(CRITERIA_TYPE_BATTLEPET_LEVEL_UP, currentPet->Level, speciesInfo->PetTypeEnum, currentPet->Species);
player->UpdateCriteria(CRITERIA_TYPE_BATTLEPET_LEVEL_UP, currentPet->Species, currentPet->Level, speciesInfo->PetTypeEnum);
player->UpdateCriteria(CRITERIA_TYPE_LEVEL_BATTLE_PET_CREDIT, speciesInfo->ID, currentPet->Level, currentPet->Species);
}
@@ -1317,7 +1317,7 @@ void PetBattle::Finish(uint32 winnerTeamID, bool aborted, bool ignoreAbandonPena
if (winnerTeamID == currentTeamID)
{
auto speciesInfo = sDB2Manager.GetSpeciesByCreatureID(InitialWildPetGUID.GetEntry());
player->UpdateCriteria(CRITERIA_TYPE_BATTLEPET_WIN, BattleType != PETBATTLE_TYPE_PVE, 0, speciesInfo ? speciesInfo->ID : 0);
player->UpdateCriteria(CRITERIA_TYPE_BATTLEPET_WIN, speciesInfo ? speciesInfo->ID : 0, BattleType != PETBATTLE_TYPE_PVE, 0);
if (speciesInfo)
player->QuestObjectiveSatisfy(speciesInfo->ID, 1, QUEST_OBJECTIVE_DEFEATBATTLEPET, InitialWildPetGUID);
}
@@ -1560,6 +1560,15 @@ void Battleground::RelocateDeadPlayers(ObjectGuid guideGuid)
bool Battleground::AddObject(uint32 type, uint32 entry, float x, float y, float z, float o, float rotation0, float rotation1, float rotation2, float rotation3, uint32 /*respawnTime*/, GOState goState)
{
// Garde-fou : voir GetBGObject. Ici l'indice sert a une ECRITURE,
// BgObjects[type] = go->GetGUID() -- plus grave encore qu'une lecture.
if (type >= BgObjects.size())
{
TC_LOG_ERROR("bg.battleground", "Battleground::AddObject: type %u hors limites (%u objets) pour le BG (carte %u, instance %u), entree %u ignoree.",
type, uint32(BgObjects.size()), m_MapId, m_InstanceID, entry);
return false;
}
// If the assert is called, means that BgObjects must be resized!
ASSERT(type < BgObjects.size());
@@ -1629,6 +1638,14 @@ bool Battleground::AddObject(uint32 type, uint32 entry, Position const& pos, flo
// It would be nice to correctly implement GO_ACTIVATED state and open/close doors in gameobject code
void Battleground::DoorClose(uint32 type)
{
// Garde-fou : voir GetBGObject.
if (type >= BgObjects.size())
{
TC_LOG_ERROR("bg.battleground", "Battleground::DoorClose: type %u hors limites (%u objets) pour le BG (carte %u, instance %u).",
type, uint32(BgObjects.size()), m_MapId, m_InstanceID);
return;
}
if (GameObject* obj = GetBgMap()->GetGameObject(BgObjects[type]))
{
// If doors are open, close it
@@ -1645,6 +1662,14 @@ void Battleground::DoorClose(uint32 type)
void Battleground::DoorOpen(uint32 type)
{
// Garde-fou : voir GetBGObject.
if (type >= BgObjects.size())
{
TC_LOG_ERROR("bg.battleground", "Battleground::DoorOpen: type %u hors limites (%u objets) pour le BG (carte %u, instance %u).",
type, uint32(BgObjects.size()), m_MapId, m_InstanceID);
return;
}
if (GameObject* obj = GetBgMap()->GetGameObject(BgObjects[type]))
{
obj->SetLootState(GO_ACTIVATED);
@@ -1657,6 +1682,33 @@ void Battleground::DoorOpen(uint32 type)
GameObject* Battleground::GetBGObject(uint32 type, bool logError)
{
// =================================================================
// SylvaniaCore : refuser les indices hors limites.
//
// SIGNALE PAR UN UTILISATEUR DE SYLVANIACORE : ses matchs de Bassin
// d'Arathi faisaient tomber le serveur, avec dans le journal
// GetBGObject: gameobject (type: 24, ... Entry: 7735234) not found
// GetBGObject: gameobject (type: 32, ... Entry: 0) not found
// Des GUID manifestement arbitraires : BgObjects ne compte que 22
// cases pour ce champ de bataille.
//
// La cause etait une indexation node*8 heritee de l'ancien Bassin
// d'Arathi (corrigee dans BattlegroundAB.cpp), mais le defaut de
// fond est ici : ces accesseurs indexent un std::vector avec une
// valeur fournie par l'appelant, SANS jamais verifier la borne. Une
// simple faute de calcul devient alors une corruption memoire, et
// le plantage survient loin de son origine.
//
// On journalise et on renvoie l'echec plutot que de lire n'importe
// ou. Meme garde-fou sur les autres accesseurs indexes du fichier.
// =================================================================
if (type >= BgObjects.size())
{
TC_LOG_ERROR("bg.battleground", "Battleground::GetBGObject: type %u hors limites (%u objets) pour le BG (carte %u, instance %u).",
type, uint32(BgObjects.size()), m_MapId, m_InstanceID);
return nullptr;
}
GameObject* obj = GetBgMap()->GetGameObject(BgObjects[type]);
if (!obj)
{
@@ -1672,6 +1724,14 @@ GameObject* Battleground::GetBGObject(uint32 type, bool logError)
Creature* Battleground::GetBGCreature(uint32 type, bool logError)
{
// Garde-fou : voir GetBGObject.
if (type >= BgCreatures.size())
{
TC_LOG_ERROR("bg.battleground", "Battleground::GetBGCreature: type %u hors limites (%u creatures) pour le BG (carte %u, instance %u).",
type, uint32(BgCreatures.size()), m_MapId, m_InstanceID);
return nullptr;
}
Creature* creature = GetBgMap()->GetCreature(BgCreatures[type]);
if (!creature)
{
@@ -1687,6 +1747,14 @@ Creature* Battleground::GetBGCreature(uint32 type, bool logError)
void Battleground::SpawnBGObject(uint32 type, uint32 respawntime)
{
// Garde-fou : voir GetBGObject.
if (type >= BgObjects.size())
{
TC_LOG_ERROR("bg.battleground", "Battleground::SpawnBGObject: type %u hors limites (%u objets) pour le BG (carte %u, instance %u).",
type, uint32(BgObjects.size()), m_MapId, m_InstanceID);
return;
}
if (Map* map = FindBgMap())
if (GameObject* obj = map->GetGameObject(BgObjects[type]))
{
@@ -1761,6 +1829,14 @@ Creature* Battleground::AddCreature(uint32 entry, uint32 type, Position const& p
bool Battleground::DelCreature(uint32 type)
{
// Garde-fou : voir GetBGObject.
if (type >= BgCreatures.size())
{
TC_LOG_ERROR("bg.battleground", "Battleground::DelCreature: type %u hors limites (%u creatures) pour le BG (carte %u, instance %u).",
type, uint32(BgCreatures.size()), m_MapId, m_InstanceID);
return false;
}
if (!BgCreatures[type])
return true;
@@ -1779,6 +1855,14 @@ bool Battleground::DelCreature(uint32 type)
bool Battleground::DelObject(uint32 type)
{
// Garde-fou : voir GetBGObject.
if (type >= BgObjects.size())
{
TC_LOG_ERROR("bg.battleground", "Battleground::DelObject: type %u hors limites (%u objets) pour le BG (carte %u, instance %u).",
type, uint32(BgObjects.size()), m_MapId, m_InstanceID);
return false;
}
if (!BgObjects[type])
return true;
@@ -155,12 +155,25 @@ void BattlegroundMgr::Update(uint32 diff)
{
// forced update for rated arenas (scan all, but skipped non rated)
TC_LOG_TRACE("bg.arena", "BattlegroundMgr: UPDATING ARENA QUEUES");
for (int qtype = BATTLEGROUND_QUEUE_2v2; qtype <= BATTLEGROUND_QUEUE_5v5; ++qtype)
// SylvaniaCore : l amont avait commente la boucle sur les brackets et fige
// le bracket a 14, qui n existe pas dans PVPDifficulty pour la carte du
// template BATTLEGROUND_AA. Resultat : aucune file cotee n etait reellement
// mise a jour, et BattlegroundQueueUpdate journalisait une erreur toutes les
// 5 secondes. On parcourt les brackets reellement declares pour cette carte.
if (Battleground* arenaTemplate = GetBattlegroundTemplate(BATTLEGROUND_AA))
{
//for (int bracket = BG_BRACKET_ID_FIRST; bracket < MAX_BATTLEGROUND_BRACKETS; ++bracket)
m_BattlegroundQueues[qtype].BattlegroundQueueUpdate(diff,
BATTLEGROUND_AA, BattlegroundBracketId(14),//bracket
BattlegroundMgr::BGArenaType(BattlegroundQueueTypeId(qtype)), true, 0);
for (int qtype = BATTLEGROUND_QUEUE_2v2; qtype <= BATTLEGROUND_QUEUE_5v5; ++qtype)
{
for (uint8 bracket = BG_BRACKET_ID_FIRST; bracket < MAX_BATTLEGROUND_BRACKETS; ++bracket)
{
if (!DB2Manager::GetBattlegroundBracketById(arenaTemplate->GetMapId(), BattlegroundBracketId(bracket)))
continue;
m_BattlegroundQueues[qtype].BattlegroundQueueUpdate(diff,
BATTLEGROUND_AA, BattlegroundBracketId(bracket),
BattlegroundMgr::BGArenaType(BattlegroundQueueTypeId(qtype)), true, 0);
}
}
}
m_NextRatedArenaUpdate = sWorld->getIntConfig(CONFIG_ARENA_RATED_UPDATE_TIMER);
@@ -938,6 +938,15 @@ should be called from Battleground::RemovePlayer function in some cases
*/
void BattlegroundQueue::BattlegroundQueueUpdate(uint32 /*diff*/, BattlegroundTypeId bgTypeId, BattlegroundBracketId bracket_id, uint8 arenaType, bool isRated, uint32 arenaRating)
{
// SylvaniaCore : garde-fou. m_QueuedGroups n a que MAX_BATTLEGROUND_BRACKETS
// entrees ; un appelant qui fabrique un bracket hors plage lisait au-dela du
// tableau (comportement indefini) des la premiere ligne de la fonction.
if (bracket_id >= MAX_BATTLEGROUND_BRACKETS)
{
TC_LOG_ERROR("bg.battleground", "Battleground: Update: bracket id %u hors plage pour le bg %u", uint32(bracket_id), uint32(bgTypeId));
return;
}
//if no players in queue - do nothing
if (m_QueuedGroups[bracket_id][BG_QUEUE_PREMADE_ALLIANCE].empty() &&
m_QueuedGroups[bracket_id][BG_QUEUE_PREMADE_HORDE].empty() &&
@@ -181,7 +181,39 @@ void CommandAB::ProcessABNodeRequirement(uint32 abNode, AIWaypoint* waypoint, Pl
{
if (!waypoint)
return;
GameObject* pBGNode = m_pBattleground->GetBGObject(abNode * 8);
// =================================================================
// SylvaniaCore : les bannieres d'Arathi sont aux indices 0 a 4.
//
// SIGNALE PAR UN UTILISATEUR DE SYLVANIACORE, journal a l'appui :
// GetBGObject: type 24 hors limites (22 objets) carte 529
// GetBGObject: type 32 hors limites (22 objets) carte 529
//
// abNode * 8 vient de l'ancien Bassin d'Arathi de TrinityCore, ou
// chaque noeud possedait huit objets. Le notre n'en a qu'UN par
// noeud : BG_AB_OBJECT_BANNER + node, soit 0 a 4. _ChangeBanner
// modifie le visuel de cette banniere unique selon l'etat.
//
// Le calcul etait donc faux pour TOUS les noeuds, pas seulement
// pour ceux qui debordaient :
// noeud 0 -> 0 banniere juste, par hasard
// noeud 1 -> 8 REGENBUFF_STABLES
// noeud 2 -> 16 SPEEDBUFF_LUMBER_MILL
// noeud 3 -> 24 hors limites
// noeud 4 -> 32 hors limites
//
// Les bots visaient des objets de bonus au lieu des bannieres, et
// ne voyaient tout simplement pas la scierie ni la mine d'or. Les
// deux appels etant proteges contre le pointeur nul, cela echouait
// en silence -- seul le garde-fou pose dans GetBGObject a fini par
// le rendre visible.
//
// Le controle de borne sur abNode manquait ici alors qu'il existe
// dans GetABFlagRangePlayerByTeam : ajoute.
// =================================================================
if (abNode >= BG_AB_BattlegroundNodes::BG_AB_DYNAMIC_NODES_COUNT)
return;
GameObject* pBGNode = m_pBattleground->GetBGObject(BG_AB_ObjectType::BG_AB_OBJECT_BANNER + abNode);
if (!pBGNode)
return;
PlayerGUIDs nodeNearPlayers = GetABFlagRangePlayerByTeam(abNode, m_TeamID);
@@ -264,7 +296,9 @@ PlayerGUIDs CommandAB::GetABFlagRangePlayerByTeam(uint32 abNode, TeamId team)
PlayerGUIDs existPlayers;
if (!m_pBattleground || abNode >= BG_AB_BattlegroundNodes::BG_AB_DYNAMIC_NODES_COUNT)
return existPlayers;
GameObject* pBGNode = m_pBattleground->GetBGObject(abNode * 8);
// Meme correction que dans ProcessABNodeRequirement : une banniere
// par noeud, aux indices 0 a 4.
GameObject* pBGNode = m_pBattleground->GetBGObject(BG_AB_ObjectType::BG_AB_OBJECT_BANNER + abNode);
if (!pBGNode)
return existPlayers;
NearPlayerList playersNearby;
@@ -731,9 +731,16 @@ GameObject const* BattlegroundAB::GetNearGameObjectFlag(const Player* player)
// cliquable si neutre, ou tenu/conteste par l equipe adverse
if (!(status == 0 || teamIndex == status % 2))
continue;
// BgObjects[node*8 + status] = banniere affichee pour cet etat (0=neutre,
// 1/2=contestee A/H, 3/4=occupee A/H)
GameObject* flag = GetBgMap()->GetGameObject(BgObjects[node * 8 + status]);
// Ce coeur n'a QU'UNE banniere par noeud, aux indices 0 a 4
// (BG_AB_OBJECT_BANNER + node) : _ChangeBanner modifie son visuel
// selon l'etat au lieu d'echanger huit objets distincts.
//
// L'indexation node*8+status ecrite ici venait de l'ancien Bassin
// d'Arathi de TrinityCore, ou chaque noeud possedait bien huit
// objets. Elle donnait 24 pour le noeud 3 et 32 pour le noeud 4,
// dans un tableau qui n'en compte que 22 : lecture hors limites,
// GUID arbitraires, et plantage selon ce qui suit le tampon.
GameObject* flag = GetBGObject(BG_AB_OBJECT_BANNER + node, false);
if (!flag || !flag->isSpawned())
continue;
float dist = player->GetDistance(flag);
+5 -1
View File
@@ -2777,7 +2777,11 @@ GameObject* WorldObject::SummonGameObject(uint32 entry, Position const& pos, Qua
go->SetVisibleBySummonerOnly(visibleBySummonerOnly);
go->SetRespawnTime(respawnTime);
if (GetTypeId() == TYPEID_PLAYER || GetTypeId() == TYPEID_UNIT) //not sure how to handle this
// Un coffre de butin ne doit pas etre rattache a son invocateur : les objets
// rattaches sont detruits avec lui (RemoveAllGameObjects), donc le coffre d'un
// boss disparaissait cote serveur des que son cadavre s'effacait -- le raid
// voyait encore le coffre mais ne pouvait plus l'ouvrir (Ragnaros, entre autres).
if ((GetTypeId() == TYPEID_PLAYER || GetTypeId() == TYPEID_UNIT) && goinfo->type != GAMEOBJECT_TYPE_CHEST)
ToUnit()->AddGameObject(go);
else
go->SetSpawnedByDefault(false);
@@ -243,7 +243,30 @@ void KillRewarder::_RewardGroup()
if (member->IsAtGroupRewardDistance(_victim))
{
_RewardPlayer(member, isDungeon);
member->UpdateCriteria(CRITERIA_TYPE_SPECIAL_PVP_KILL, 1, 0, 0, _victim);
// =============================================
// SylvaniaCore : ce credit est JcJ.
//
// SIGNALE EN JEU : « j ai attaque un petit groupe
// de demons et j ai recu le haut fait Courroux
// de l Alliance, totalement hors sujet ». Ce
// haut fait demande de tuer cinq joueurs de la
// Horde dans chaque grande ville.
//
// La ligne suivante creditait un meurtre JcJ a
// chaque membre du groupe pour N IMPORTE QUELLE
// mort, creatures comprises. Elle ne se voyait
// pas tant qu on jouait seul : ce bloc n est
// parcouru qu en groupe. L escorte de
// mercenaires l a mise au jour.
//
// TrinityCore ne fait pas cette mise a jour du
// tout, ni en 3.3.5 ni en master : c est un
// ajout local, pose sans le garde-fou _isPvP
// que ce fichier emploie partout ailleurs.
// =============================================
if (_isPvP)
member->UpdateCriteria(CRITERIA_TYPE_SPECIAL_PVP_KILL, 1, 0, 0, _victim);
}
}
}
+7 -7
View File
@@ -2092,8 +2092,7 @@ void Player::ProcessDelayedOperations()
{
uint8 teamID = battle->Teams[PETBATTLE_TEAM_1]->OwnerGuid == GetGUID() ? PETBATTLE_TEAM_1 : PETBATTLE_TEAM_2;
PetBattleRequest request;
memcpy(&request, &battle->PvPMatchMakingRequest, sizeof(PetBattleRequest)); //@TODO check that
PetBattleRequest request = battle->PvPMatchMakingRequest;
auto& matchMakingRequest = battle->PvPMatchMakingRequest;
@@ -20227,14 +20226,15 @@ bool Player::QuestObjectiveActiveInPlayerByObject(uint32 objectId)
continue;
for (QuestObjective const& obj : quest->GetObjectives())
{
if ((uint32)obj.ObjectID == objectId)
{
return true;
break;
}
}
}
// SylvaniaCore : sans ce return, sortir de la boucle etait un comportement
// indefini -- GCC en deduisait que la boucle ne pouvait pas se terminer et
// supprimait le test q < MAX_QUEST_LOG_SIZE. Le slot grimpait au-dela de 25
// jusqu a sortir du tableau de valeurs du joueur (ASSERT dans GetUInt32Value).
return false;
}
PlayerPetData* Player::GetPlayerPetDataById(uint32 petId)
+11 -8
View File
@@ -642,11 +642,11 @@ void Garrison::AddMission(uint32 garrMissionId)
reward.FollowerXP = 0;
reward.BonusAbilityID = 0;
reward.Unknown = 1118739;
std::vector<GarrssionMissionReward>* fRewards = sObjectMgr->GetGarrssionMissionReward(garrMissionId);
if (fRewards)
std::vector<GarrssionMissionReward> fRewards = sObjectMgr->GetGarrssionMissionReward(garrMissionId);
if (!fRewards.empty())
{
Trinity::Containers::RandomShuffle(*fRewards);
for (auto _reward : *fRewards)
Trinity::Containers::RandomShuffle(fRewards);
for (auto _reward : fRewards)
{
// if (_reward.RewardType == GarrisonMission::MissionRewardType::Item)
{
@@ -1086,13 +1086,16 @@ void Garrison::CalculateMissonBonusRoll(uint32 garrMissionId)
void Garrison::RewardMission(Mission* mission, bool withOvermaxReward)
{
auto rewardLists = { mission->Rewards };
// SylvaniaCore : reaffecter une initializer_list ne prolonge pas la duree de
// vie du tableau sous-jacent -- la boucle parcourait un temporaire detruit.
std::vector<decltype(&mission->Rewards)> rewardLists;
rewardLists.push_back(&mission->Rewards);
if (withOvermaxReward)
rewardLists = { mission->Rewards, mission->BonusRewards };
rewardLists.push_back(&mission->BonusRewards);
for (auto rewards : rewardLists)
for (auto const* rewards : rewardLists)
{
for (WorldPackets::Garrison::GarrisonMissionReward reward : rewards)
for (WorldPackets::Garrison::GarrisonMissionReward reward : *rewards)
{
if (reward.ItemID)
GetOwner()->AddItem(reward.ItemID, reward.ItemQuantity);
@@ -169,12 +169,12 @@ bool WodGarrison::Upgrade()
_owner->SendGarrisonRemoteInfo();
PhasingHandler::OnConditionChange(_owner);
TeleportOwnerAndPlayMovie();
TeleportOwnerToGarrison();
return true;
}
void WodGarrison::TeleportOwnerAndPlayMovie() const
void WodGarrison::TeleportOwnerToGarrison() const
{
Position WodGarrisonEntrancePositions[2][3] =
{
@@ -192,12 +192,13 @@ void WodGarrison::TeleportOwnerAndPlayMovie() const
}
};
_owner->AddMovieDelayedAction(_siteLevel->UpgradeMovieID, [this, WodGarrisonEntrancePositions]
{
_owner->TeleportTo(_siteLevel->MapID, WodGarrisonEntrancePositions[GetFaction()][_siteLevel->GarrLevel - 1]);
});
_owner->SendMovieStart(_siteLevel->UpgradeMovieID);
/// Le film d'etablissement du fief (189 Horde / 192 Alliance, et 190/191/193/194
/// aux niveaux suivants) fige le client : le joueur doit se reconnecter. Comme il
/// n'envoie alors jamais CMSG_COMPLETE_MOVIE, l'action differee ne partait pas et
/// il n'etait jamais teleporte dans son fief. On teleporte donc directement.
/// Remettre AddMovieDelayedAction + SendMovieStart(_siteLevel->UpgradeMovieID) le
/// jour ou la cause du gel cote client sera connue.
_owner->TeleportTo(_siteLevel->MapID, WodGarrisonEntrancePositions[GetFaction()][_siteLevel->GarrLevel - 1]);
}
void WodGarrison::Delete()
@@ -84,7 +84,7 @@ public:
bool Create(uint32 garrSiteId) override;
bool CanUpgrade(bool checkCost = true);
bool Upgrade();
void TeleportOwnerAndPlayMovie() const;
void TeleportOwnerToGarrison() const;
void Delete() override;
void Enter() override;
+5 -1
View File
@@ -319,7 +319,11 @@ Battlepay::Product const& BattlePayDataStoreMgr::GetProduct(uint32 productID) co
{
if (ProductExist(productID))
return _products.at(productID);
return{};
// SylvaniaCore : "return{}" rendait une reference sur un temporaire detruit
// des le retour ; les huit appelants lisaient de la memoire morte.
static Battlepay::Product const emptyProduct{};
return emptyProduct;
}
Battlepay::DisplayInfo const* BattlePayDataStoreMgr::GetDisplayInfo(uint32 id) const
+5 -5
View File
@@ -3307,7 +3307,10 @@ void ObjectMgr::LoadGarrssionMissionReward()
TC_LOG_INFO("server.loading", ">> Loaded garrssion_mission_reward in %u ms", GetMSTimeDiffToNow(oldMSTime));
}
std::vector<GarrssionMissionReward>* ObjectMgr::GetGarrssionMissionReward(uint32 id)
// SylvaniaCore : cette fonction rendait l adresse d un vecteur local. L appelant
// (Garrison::...) melangeait puis parcourait un objet deja detruit -- ecriture et
// lecture dans une pile liberee. On rend desormais le vecteur par valeur.
std::vector<GarrssionMissionReward> ObjectMgr::GetGarrssionMissionReward(uint32 id)
{
std::vector<GarrssionMissionReward> _reward;
for (auto& __reward : GarrssionMissionRewardMap)
@@ -3315,10 +3318,7 @@ std::vector<GarrssionMissionReward>* ObjectMgr::GetGarrssionMissionReward(uint32
if (__reward.MissionId == id)
_reward.push_back(__reward);
}
if (_reward.size() > 0)
return &_reward;
else
return nullptr;
return _reward;
}
void ObjectMgr::LoadVehicleTemplateAccessories()
+1 -1
View File
@@ -999,7 +999,7 @@ class TC_GAME_API ObjectMgr
ItemTemplateContainer const* GetItemTemplateStore() const { return &_itemTemplateStore; }
void LoadGarrssionMissionReward();
std::vector<GarrssionMissionReward>* GetGarrssionMissionReward(uint32 id);
std::vector<GarrssionMissionReward> GetGarrssionMissionReward(uint32 id);
std::vector<GarrssionMissionReward> GarrssionMissionRewardMap;
ScriptParams const& GetScriptParam(ObjectGuid::LowType lowGuid) { return _scriptParamContainer[lowGuid]; }
+20 -1
View File
@@ -1172,7 +1172,20 @@ void Group::GroupLoot(Loot* loot, WorldObject* lootedObject)
if (i->freeforall)
continue;
item = ASSERT_NOTNULL(sObjectMgr->GetItemTemplate(i->itemid));
// Les devises (Item negatif dans les tables de butin) occupent le meme tableau
// que les objets mais n'ont pas de modele d'objet. Les laisser arriver jusqu'a
// l'ASSERT tuait le worldserver des qu'un groupe ouvrait le butin d'un boss qui
// en lache -- ceux de la Prison violette, entre autres.
if (i->type != LOOT_ITEM_TYPE_ITEM)
continue;
item = sObjectMgr->GetItemTemplate(i->itemid);
if (!item)
{
TC_LOG_ERROR("sql.sql", "Group::GroupLoot: objet %u introuvable dans les donnees, ignore dans le butin de %s",
i->itemid, lootedObject->GetGUID().ToString().c_str());
continue;
}
//roll for over-threshold item if it's one-player loot
if (item->GetQuality() >= uint32(m_lootThreshold))
@@ -1247,7 +1260,13 @@ void Group::GroupLoot(Loot* loot, WorldObject* lootedObject)
if (!i->follow_loot_rules)
continue;
if (i->type != LOOT_ITEM_TYPE_ITEM)
continue;
item = sObjectMgr->GetItemTemplate(i->itemid);
if (!item)
continue;
Roll* r = new Roll(*i);
for (GroupReference* itr = GetFirstMember(); itr != NULL; itr = itr->next())
+30 -1
View File
@@ -199,7 +199,36 @@ void WorldSession::HandleChatMessage(ChatMsg type, uint32 lang, std::string msg,
if (msg.empty())
return;
if (ChatHandler(this).ParseCommands(msg.c_str()))
// =================================================================
// ORDRE_AUX_COMPAGNONS
//
// SIGNALE EN JEU : « !summon repond "You can't teleport yourself to
// yourself", et !follow repond "There is no such command" ».
//
// ChatHandler::ParseCommands traite le point d'exclamation comme un
// prefixe de commande au meme titre que le point :
// if (text[0] != '!' && text[0] != '.') return false;
// Il intercepte donc tout ordre avant qu'il n'atteigne le canal de
// groupe, ou Group::ProcessGroupBotCommand l'attend. Le systeme
// d'ordres aux playerbots etait inaccessible depuis toujours --
// « !summon » partait vers la commande de maitre de jeu du meme nom.
//
// On laisse passer les messages en « ! » lorsque le joueur commande
// effectivement des bots. Un maitre de jeu sans compagnon garde ses
// commandes en « ! » ; celui qui en a passe par le point, qui reste
// le prefixe principal.
bool ordreAuxCompagnons = false;
if (!msg.empty() && msg[0] == '!')
if (Group* groupe = GetPlayer()->GetGroup())
for (GroupReference* itr = groupe->GetFirstMember(); itr != nullptr; itr = itr->next())
if (Player* membre = itr->GetSource())
if (membre->IsPlayerBot())
{
ordreAuxCompagnons = true;
break;
}
if (!ordreAuxCompagnons && ChatHandler(this).ParseCommands(msg.c_str()))
return;
// Strip invisible characters for non-addon messages
@@ -96,6 +96,10 @@ void WorldSession::HandleQuestgiverHelloOpcode(WorldPackets::Quest::QuestGiverHe
if (sScriptMgr->OnGossipHello(_player, creature))
return;
// Un PNJ purement donneur de quete (sans drapeau papotage) recoit CMSG_QUESTGIVER_HELLO
// au lieu de CMSG_GOSSIP_HELLO : sans cet appel, les objectifs \"Parler a\" ne sont jamais valides.
_player->TalkedToCreature(creature->GetEntry(), creature->GetGUID());
_player->PrepareGossipMenu(creature, creature->GetCreatureTemplate()->GossipMenuId, true);
_player->SendPreparedGossip(creature);
+34 -1
View File
@@ -714,7 +714,40 @@ void InstanceScript::DoUpdateCriteria(CriteriaTypes type, uint32 miscValue1 /*=
void InstanceScript::DoSendEventScenario(uint32 eventId /*= 0*/)
{
DoUpdateCriteria(CRITERIA_TYPE_SEND_EVENT_SCENARIO, eventId, 0, nullptr);
// =================================================================
// SylvaniaCore : un evenement de scenario ne compte QU UNE FOIS.
//
// SIGNALE EN JEU : « un petit groupe de demons m a valide les 33/33
// et les 3/3 gangreseigneurs d un coup ».
//
// DoUpdateCriteria diffuse a CHAQUE joueur de l instance, et
// Player::UpdateCriteria transmet ensuite au scenario :
// if (Scenario* scenario = GetScenario())
// scenario->UpdateCriteria(...);
// Or ce compteur est PARTAGE par toute l instance. Chaque mort etait
// donc comptee autant de fois qu il y avait de joueurs presents --
// cinq, avec une escorte de mercenaires. Sept demons suffisaient a
// remplir un objectif qui en demande trente-trois.
//
// Le defaut restait invisible tant qu on jouait seul : un joueur,
// un credit. L escorte l a mis au jour.
//
// On credite desormais le scenario une seule fois, en lui passant
// un joueur de reference pour les conditions qui en dependent.
// =================================================================
Scenario* scenario = instance->GetInstanceScenario();
if (!scenario)
return;
Map::PlayerList const& liste = instance->GetPlayers();
for (Map::PlayerList::const_iterator i = liste.begin(); i != liste.end(); ++i)
{
if (Player* reference = i->GetSource())
{
scenario->UpdateCriteria(CRITERIA_TYPE_SEND_EVENT_SCENARIO, eventId, 0, 0, nullptr, reference);
return;
}
}
}
// Start timed achievement for all players in instance
+1 -1
View File
@@ -570,7 +570,7 @@ class TC_GAME_API InstanceScript : public ZoneScript
uint64 m_InstanceGuid;
uint32 m_BeginningTime;
uint32 m_ScenarioID;
uint8 m_ScenarioStep;
uint32 m_ScenarioStep; // SylvaniaCore : ID de ScenarioStep, un uint8 tronquait la valeur
uint32 m_LastResetTime;
uint8 m_ChallengeLevel;
uint8 m_DeathCount;
+118 -25
View File
@@ -36,6 +36,24 @@
#include "World.h"
#include "WorldSession.h"
namespace
{
// Quelques pas devant la structure, angle tire au hasard pour que les
// recrues ne s empilent pas, face au portail, hauteur recalee au sol.
Position BuildPortalExit(Position const& portal, Player* bot)
{
float const angle = portal.GetOrientation() + frand(-1.0f, 1.0f);
float const distance = frand(1.5f, 3.0f);
float x = portal.GetPositionX() + distance * std::cos(angle);
float y = portal.GetPositionY() + distance * std::sin(angle);
float z = portal.GetPositionZ();
if (bot)
if (Map* map = bot->GetMap())
map->GetHeight(bot->GetPhaseShift(), x, y, z);
return Position(x, y, z, angle + float(M_PI));
}
}
MercenaryMgr::MercenaryMgr() :
m_enabled(false), m_cost(100 * MERCENARY_COPPER_PER_GOLD), m_maxPerPlayer(MERCENARY_HARD_CAP),
m_minLevel(10), m_updateTimer(0), m_releasing(false)
@@ -343,7 +361,7 @@ bool MercenaryMgr::FindCandidate(Player* owner, uint8 role, uint32& accountId, u
return false;
}
MercenaryResult MercenaryMgr::Summon(Player* owner, uint8 role, Creature* portal)
MercenaryResult MercenaryMgr::Summon(Player* owner, uint8 role, Creature* portal, bool freeOfCharge)
{
if (!m_enabled)
return MERC_ERR_DISABLED;
@@ -378,7 +396,7 @@ MercenaryResult MercenaryMgr::Summon(Player* owner, uint8 role, Creature* portal
return MERC_ERR_GROUP_FULL;
}
if (!owner->HasEnoughMoney(uint64(m_cost)))
if (!freeOfCharge && !owner->HasEnoughMoney(uint64(m_cost)))
return MERC_ERR_NO_MONEY;
uint32 accountId = 0;
@@ -392,8 +410,9 @@ MercenaryResult MercenaryMgr::Summon(Player* owner, uint8 role, Creature* portal
if (!session)
return MERC_ERR_NO_BOT;
// Le mercenaire existe : on encaisse.
owner->ModifyMoney(-int64(m_cost));
// Le mercenaire existe : on encaisse, sauf recrutement offert.
if (!freeOfCharge)
owner->ModifyMoney(-int64(m_cost));
uint32 const level = PlayerBotSetting::CheckMaxLevel(owner->getLevel());
@@ -441,8 +460,9 @@ MercenaryResult MercenaryMgr::Summon(Player* owner, uint8 role, Creature* portal
}
m_contracts.push_back(contract);
TC_LOG_INFO("server.worldserver", "Mercenaires: %s a paye %u po pour un %s (compte bot %u).",
owner->GetName().c_str(), GetCostGold(), GetRoleName(role), accountId);
TC_LOG_INFO("server.worldserver", "Mercenaires: %s engage un %s (compte bot %u) -- %s.",
owner->GetName().c_str(), GetRoleName(role), accountId,
freeOfCharge ? "recrutement offert" : "100 po preleves");
return MERC_OK;
}
@@ -638,28 +658,44 @@ void MercenaryMgr::Update(uint32 diff)
// le mercenaire a son ancienne place aux yeux de tout le monde.
if (it->summonPending)
{
if (BotGroupAI* groupAI = dynamic_cast<BotGroupAI*>(bot->GetAI()))
{
if (it->hasPortal)
{
// Il franchit le seuil : quelques pas devant la structure,
// l angle varie pour que quatre mercenaires ne se marchent
// pas dessus, et la hauteur est recalee sur le terrain.
float const angle = it->portalPos.GetOrientation() + frand(-1.0f, 1.0f);
float const distance = frand(1.5f, 3.0f);
float x = it->portalPos.GetPositionX() + distance * std::cos(angle);
float y = it->portalPos.GetPositionY() + distance * std::sin(angle);
float z = it->portalPos.GetPositionZ();
if (Map* map = bot->GetMap())
map->GetHeight(bot->GetPhaseShift(), x, y, z);
// Est-il arrive ? Le point de rendez-vous est le portail quand
// il y en a un, l employeur sinon.
bool arrive = false;
if (it->hasPortal)
arrive = bot->GetMapId() == it->portalMap
&& bot->GetExactDist2d(it->portalPos.GetPositionX(),
it->portalPos.GetPositionY()) < MERCENARY_ARRIVAL_RANGE;
else
arrive = bot->GetMapId() == owner->GetMapId()
&& bot->GetExactDist2d(owner) < MERCENARY_ARRIVAL_RANGE;
// Il fait face au portail dont il sort.
Position spot(x, y, z, angle + float(M_PI));
groupAI->TeleportToPoint(it->portalMap, spot);
}
if (arrive)
it->summonPending = false;
else if (it->summonCheckTimer > 0)
--it->summonCheckTimer;
else if (it->summonAttempts >= MERCENARY_SUMMON_RETRIES)
{
// On cesse d insister : mieux vaut un mercenaire annonce
// perdu qu un mercenaire qu on croit present.
it->summonPending = false;
ChatHandler(owner->GetSession()).PSendSysMessage(
"|cffff4444[Portail]|r %s n'a pas pu vous rejoindre.", bot->GetName().c_str());
TC_LOG_ERROR("server.worldserver",
"Mercenaires: %s n'a pas rejoint %s apres %u rappels (carte %u).",
bot->GetName().c_str(), owner->GetName().c_str(),
uint32(it->summonAttempts), bot->GetMapId());
}
else if (BotGroupAI* groupAI = dynamic_cast<BotGroupAI*>(bot->GetAI()))
{
// Repli : sans portail connu, ou si l armement immediat a
// echoue, le mercenaire rejoint simplement son employeur.
if (it->hasPortal)
groupAI->TeleportToPoint(it->portalMap, BuildPortalExit(it->portalPos, bot));
else
groupAI->ProcessBotCommand(owner, "!summon");
it->summonPending = false;
++it->summonAttempts;
it->summonCheckTimer = MERCENARY_SUMMON_RECHECK;
}
}
@@ -733,6 +769,63 @@ void MercenaryMgr::Update(uint32 diff)
PlayerBotMgr::SwitchPlayerBotAI(bot, PlayerBotAIType::PBAIT_GROUP, true);
// =============================================================
// RERESOLUTION_DES_SORTS
//
// MESURE (sonde SORTDBG, 09/09/2026) : sur quatre mercenaires tous
// montes au niveau 110, l IA avait resolu ses sorts au niveau 1 pour
// l un, au niveau 100 pour deux autres. Seul le quatrieme, re-resolu
// par hasard a 110, n avait plus que quatre capacites indisponibles
// -- et les quatre appartenaient a une autre specialisation, donc
// parfaitement normales.
//
// Les tables de sorts ne sont donc pas en cause. Le defaut est un
// probleme d ordre : chaque IA de classe resout ses poignees UNE
// fois, a sa construction, via FindMaxRankSpellByExist ; ce qui n
// est pas connu a cet instant reste eteint a vie, chaque usage etant
// garde par un « if (poignee) ».
//
// Le core prevoit pourtant la reparation : quand la mise en place s
// acheve, PlayerBotSetting pousse un BGSType_DelayLevelup qui
// reapprend les sorts et rappelle InitializeSpells. Mais
// OnLevelupToBotAI() commence par un dynamic_cast<BotGroupAI*> et ne
// fait rien si l IA de groupe n existe pas encore -- or nous venons
// seulement de la creer, la ligne au-dessus. La tache partait avant
// sa cible et tombait dans le vide.
//
// CORRECTION DU CORRECTIF (audit du 09/09) : la premiere version
// poussait un BGSType_DelayLevelup dans la file du bot. Mesure faite
// sur quatre mercenaires : deux repares, deux non. En cause,
// PushScheduleToQueue, qui jette SILENCIEUSEMENT une tache dans deux
// cas -- si une du meme type est deja en file, et, pour ce type
// precisement, si IsSettingFinish() est faux. La reparation ne
// partait donc qu au petit bonheur.
//
// On appelle directement. L IA de groupe vient d etre creee a la
// ligne precedente : le dynamic_cast<BotGroupAI*> qui ouvre
// OnLevelupToBotAI() aboutit forcement, et InitializeSpells re-resout
// les poignees contre le niveau, la specialisation et le grimoire
// definitifs. Rien ne peut plus l escamoter.
// =============================================================
bot->OnLevelupToBotAI();
// Des maintenant, pas au tick suivant : l IA de groupe, en decouvrant
// un maitre lointain, armerait le sien vers l employeur, et SetTeleport
// refuse d ecraser un teleport en cours. Notre ordre serait perdu et le
// mercenaire apparaitrait sur le joueur plutot que sur le portail.
if (it->hasPortal)
{
if (BotGroupAI* groupAI = dynamic_cast<BotGroupAI*>(bot->GetAI()))
{
groupAI->TeleportToPoint(it->portalMap, BuildPortalExit(it->portalPos, bot));
// L ordre est parti, mais rien ne dit encore qu il aboutira :
// on le compte comme une premiere tentative et la verification
// d arrivee prend le relais au tick suivant.
it->summonAttempts = 1;
it->summonCheckTimer = MERCENARY_SUMMON_RECHECK;
}
}
ChatHandler(owner->GetSession()).PSendSysMessage(
"|cff00ff00[Portail]|r %s, %s mercenaire, répond à votre appel.",
bot->GetName().c_str(), GetRoleName(it->role));
+26 -2
View File
@@ -40,6 +40,18 @@ class Player;
#define MERCENARY_GROUP_SIZE 5
#define MERCENARY_HARD_CAP (MERCENARY_GROUP_SIZE - 1)
#define MERCENARY_SUMMON_TIMEOUT 60 // secondes avant abandon + remboursement
// SIGNALE EN JEU : « Semid n est meme pas vers moi, elle est a l autre bout de
// la map ». L ordre de rappel partait une seule fois et le contrat le tenait
// aussitot pour honore. Quand il se perdait -- IA pas encore prete, teleport
// deja en cours, changement de carte -- le mercenaire restait a sa derniere
// position enregistree pour toute la duree du contrat.
//
// On verifie desormais l arrivee, et on renouvelle l ordre tant qu elle ne
// vient pas.
#define MERCENARY_ARRIVAL_RANGE 80.0f // distance au-dela de laquelle il n est pas arrive
#define MERCENARY_SUMMON_RETRIES 4 // ordres de rappel au maximum
#define MERCENARY_SUMMON_RECHECK 3 // secondes entre deux verifications
#define MERCENARY_COPPER_PER_GOLD 10000
enum MercenaryStage
@@ -66,7 +78,8 @@ enum MercenaryResult
struct MercenaryContract
{
MercenaryContract() : accountId(0), role(0), stage(MERC_STAGE_SUMMONING), waitSeconds(0),
pendingRelease(false), summonPending(false), hasPortal(false), portalMap(0) { }
pendingRelease(false), summonPending(false), summonAttempts(0),
summonCheckTimer(0), hasPortal(false), portalMap(0) { }
uint32 accountId; // compte bot reserve
ObjectGuid ownerGuid; // joueur qui a paye
@@ -84,6 +97,12 @@ struct MercenaryContract
// groupe aura reconnu son maitre.
bool summonPending;
// Ordres de rappel deja emis, et delai avant de reverifier l arrivee. Un
// teleport de bot se joue en trois echanges : reverifier au tick suivant
// conclurait a un echec alors qu il est simplement en cours de route.
uint8 summonAttempts;
uint32 summonCheckTimer;
// Le portail d ou part l invocation : le mercenaire doit en sortir, pas
// se materialiser aux pieds de son employeur reste en retrait.
bool hasPortal;
@@ -110,7 +129,12 @@ class TC_GAME_API MercenaryMgr
// Verifie, encaisse et lance l invocation. Le prelevement n a lieu que
// si un mercenaire du role demande a effectivement ete reserve.
// "portal" est la structure invoquante : le mercenaire en sortira.
MercenaryResult Summon(Player* owner, uint8 role, Creature* portal = nullptr);
// « freeOfCharge » saute la verification et le prelevement : il sert
// aux recrutements que le jeu offre, comme l escorte fournie a
// l entree d un scenario. Le contrat reste identique par ailleurs,
// rupture au premier depart du groupe comprise.
MercenaryResult Summon(Player* owner, uint8 role, Creature* portal = nullptr,
bool freeOfCharge = false);
void Update(uint32 diff);
@@ -105,6 +105,13 @@ void TargetedMovementGeneratorMedium<T, D>::_setTargetLocation(T* owner, bool up
bool forceDest = (owner->GetTypeId() == TYPEID_UNIT && owner->ToCreature()->IsPet()
&& owner->HasUnitState(UNIT_STATE_FOLLOW));
// Une creature volante n est pas tenue par le maillage de navigation. Sans
// ce raccourci elle ne trouve aucun chemin vers une cible posee sur un
// relief non navigable, se marque injoignable, puis evade au bout de
// CREATURE_NOPATH_EVADE_TIME en lachant son proprietaire de butin.
if (!forceDest && owner->GetTypeId() == TYPEID_UNIT && owner->ToCreature()->CanFly())
forceDest = true;
if (!forceDest && owner->GetTypeId() == TYPEID_UNIT && !owner->IsPlayerBot() && !i_target->IsPlayerBot())
{
//TC_LOG_ERROR("misc", "SPMOVE1 ");
+1 -1
View File
@@ -58,7 +58,7 @@ class TC_GAME_API FieldActing
{
public:
FieldActing(ACTING_TYPE aType) : m_ActingType(aType) {}
~FieldActing() {}
virtual ~FieldActing() {} // SylvaniaCore : classe polymorphe detruite par pointeur de base
bool MatchActingType(ACTING_TYPE aType) { return m_ActingType == aType; }
virtual void Update() {}
+29 -4
View File
@@ -405,7 +405,30 @@ bool PlayerBotSession::ProcessSetting(BotGlobleSchedule& schedule)
// heritage de l epoque ou LearnTalents() etait un corps vide.
PlayerTalentMap const* talents = player->GetTalentMap(player->GetActiveTalentGroup());
bool const hasTalents = talents && talents->size() >= player->CalculateTalentsTiers();
if (hasTalents && (schedule.parameter3 >= 4 || (player->FindTalentType() + 1 == schedule.parameter3)))
// SIGNALE EN JEU : « Kaerbrus n a meme pas d equipement ».
// Constate en base : niveau 110, 76 sorts, ZERO piece portee.
//
// Ce raccourci evite un re-level complet a un bot deja au bon
// niveau, avec ses talents et la bonne specialisation. Mais il
// saute du meme coup les etapes 6 a 10 de UpdateReset(), qui sont
// justement l habillage. Un mercenaire ayant perdu son equipement
// -- contrat precedent interrompu, re-level avorte -- ne le
// retrouvait donc JAMAIS : a chaque embauche, le raccourci
// concluait que tout allait bien et le renvoyait nu.
//
// On verifie donc qu il est reellement habille avant de couper.
// Dix pieces sur les dix-neuf emplacements : de quoi distinguer un
// bot equipe d un bot depouille, sans exiger un sans-faute (la
// seconde babiole et l arme de jet manquent souvent, legitimement).
uint32 piecesPortees = 0;
for (uint8 emplacement = EQUIPMENT_SLOT_START; emplacement < EQUIPMENT_SLOT_END; ++emplacement)
if (player->GetItemByPos(INVENTORY_SLOT_BAG_0, emplacement))
++piecesPortees;
bool const correctementEquipe = (piecesPortees >= 10);
if (hasTalents && correctementEquipe &&
(schedule.parameter3 >= 4 || (player->FindTalentType() + 1 == schedule.parameter3)))
return true;
}
}
@@ -545,7 +568,8 @@ bool PlayerBotSession::ProcessLeaveBG(BotGlobleSchedule& schedule)
PlayerBotMgr::SwitchPlayerBotAI(player, PlayerBotAIType::PBAIT_FIELD, true);
WorldPackets::Battleground::BattlefieldLeave leave(std::move(WorldPacket()));
WorldPacket opcode(CMSG_BATTLEFIELD_LEAVE);
WorldPackets::Battleground::BattlefieldLeave leave(std::move(opcode));
HandleBattlefieldLeaveOpcode(leave);
//HandleWorldPortAck();
return false;
@@ -571,7 +595,7 @@ bool PlayerBotSession::ProcessInAAQueue(BotGlobleSchedule& schedule)
return false;
}
WorldPacket packet(CMSG_BATTLEMASTER_JOIN_SKIRMISH);
WorldPacket packet(CMSG_BATTLEMASTER_JOIN_ARENA);
WorldPackets::Battleground::BattlemasterJoinArena cmd(std::move(packet));
//cmd.RolesMask = schedule.parameter1;
//cmd.Bracket = schedule.parameter2;
@@ -688,7 +712,8 @@ bool PlayerBotSession::ProcessLeaveAA(BotGlobleSchedule& schedule)
//opcode << un16;
//HandleBattlefieldLeaveOpcode(opcode);
//HandleMoveWorldportAckOpcode();
WorldPackets::Battleground::BattlefieldLeave leave(std::move(WorldPacket()));
WorldPacket opcode(CMSG_BATTLEFIELD_LEAVE);
WorldPackets::Battleground::BattlefieldLeave leave(std::move(opcode));
HandleBattlefieldLeaveOpcode(leave);
//HandleWorldPortAck();
return false;
+24 -1
View File
@@ -180,7 +180,30 @@ WorldSession::~WorldSession()
while (_recvQueue.next(packet))
delete packet;
LoginDatabase.PExecute("UPDATE account SET online = 0 WHERE id = %u;", GetAccountId()); // One-time query
// =================================================================
// SylvaniaCore : ne plus ecrire en base une fois l'arret engage.
//
// Chaque arret du serveur produisait un vidage memoire de 120 Mo :
// #4 World::~World()
// #3 PlayerBotSession::~PlayerBotSession()
// #2 WorldSession::~WorldSession()
// #1 DatabaseWorkerPool<LoginDatabaseConnection>::Execute()
// #5 __run_exit_handlers
// Le monde est detruit par les gestionnaires de sortie, donc APRES
// le demontage du reservoir de connexions : la requete ci-dessous
// ecrivait dans un objet deja liberé.
//
// Ces faux plantages noient les vrais -- on a passe du temps cette
// semaine a trier des vidages qui n'etaient que des arrets propres.
//
// Sauter la requete est sans consequence : Main.cpp remet DEJA tous
// les comptes et personnages du royaume hors ligne au demarrage.
//
// IsStopped() est statique et lit un booleen statique : il reste
// consultable pendant la destruction, contrairement a sWorld.
// =================================================================
if (!World::IsStopped())
LoginDatabase.PExecute("UPDATE account SET online = 0 WHERE id = %u;", GetAccountId());
}
bool WorldSession::PlayerDisconnected() const
+10 -2
View File
@@ -1038,7 +1038,7 @@ class TC_GAME_API WorldSession
WorldSession(uint32 id, std::string&& name, uint32 battlenetAccountId, std::shared_ptr<WorldSocket> sock, AccountTypes sec, uint8 expansion, time_t mute_time,
std::string os, LocaleConstant locale, uint32 recruiter, bool isARecruiter, std::string&& battlenetAccountName);
~WorldSession();
virtual ~WorldSession(); // SylvaniaCore : PlayerBotSession en derive, un delete sur WorldSession* ne detruisait que la moitie de l objet
void SetAddress(std::string mybot)
{
@@ -1597,7 +1597,15 @@ class TC_GAME_API WorldSession
void HandleMissileTrajectoryCollision(WorldPackets::Spells::MissileTrajectoryCollision& packet);
void HandleUpdateMissileTrajectory(WorldPackets::Spells::UpdateMissileTrajectory& packet);
bool HasSocket() { return m_Socket != NULL; }
// SylvaniaCore : m_Socket est un tableau -- le comparer a NULL testait son
// adresse, donc renvoyait toujours true (y compris pour un playerbot).
bool HasSocket() const
{
for (std::shared_ptr<WorldSocket> const& socket : m_Socket)
if (socket)
return true;
return false;
}
virtual bool IsBotSession() { return false; }
virtual bool HasSchedules() { return false; }
virtual bool HasBGSchedule() { return false; }
+1 -1
View File
@@ -4545,7 +4545,7 @@ void Spell::EffectGrantBattlePetLevel(SpellEffIndex effIndex)
BattlePetSpeciesEntry const* speciesInfo = sBattlePetSpeciesStore.LookupEntry(battlePet->Species);
if (speciesInfo)
{
player->UpdateCriteria(CRITERIA_TYPE_BATTLEPET_LEVEL_UP, battlePet->Level, speciesInfo->PetTypeEnum, battlePet->Species);
player->UpdateCriteria(CRITERIA_TYPE_BATTLEPET_LEVEL_UP, battlePet->Species, battlePet->Level, speciesInfo->PetTypeEnum);
player->UpdateCriteria(CRITERIA_TYPE_LEVEL_BATTLE_PET_CREDIT, speciesInfo->ID, battlePet->Level, battlePet->Species);
}
@@ -89,7 +89,7 @@ struct boss_admiral_svirax : public BossAI
void Initialize()
{
SetDungeonEncounterID,(2070);
SetDungeonEncounterID(2070);
}
void DamageTaken(Unit* done_by, uint32 &damage) override
@@ -244,7 +244,6 @@ struct boss_admiral_svirax : public BossAI
uint8 PHASE_01;
uint8 PHASE_02;
uint8 PHASE_03;
bool SetDungeonEncounterID;
bool IsLock;
};
@@ -1540,7 +1540,7 @@ public:
};
bool OnQuestAccept(Player* player, Creature* /*creature*/, Quest const* quest) override
{
if (quest->GetQuestId() == QUEST_STOP_GULDAN_H || QUEST_STOP_GULDAN_A)
if (quest->GetQuestId() == QUEST_STOP_GULDAN_H || quest->GetQuestId() == QUEST_STOP_GULDAN_A)
player->GetSceneMgr().PlayScene(SCENE_GULDAN_STEAL_ILLIDAN_ID);
return true;
}
@@ -253,7 +253,7 @@ class boss_new_vizaduum : public CreatureScript
if (!_isFlying)
return;
if (type == POINT_MOTION_TYPE || WAYPOINT_MOTION_TYPE)
if (type == POINT_MOTION_TYPE || type == WAYPOINT_MOTION_TYPE)
{
switch (id)
{
@@ -0,0 +1,802 @@
/*
* Scenario : Assaut du Rivage brise (ScenarioID 1280, carte 1666)
*
* Contenu de la 7.2, a ne pas confondre avec le scenario d'INTRODUCTION
* de la 7.0 (ScenarioID 786, carte 1460, scenario_broken_shore_intro.cpp)
* ou l'on suit Varian, Vol'jin, Krosus et Gul'dan. Celui-ci est l'assaut
* mene depuis Dalaran, et c'est LUI dont dependent les quetes bloquees
* 45102 « Begin the Assault » et 46734 « Assault on Broken Shore ».
*
* PORTAGE, PAS TRANSCRIPTION
* Source : dufernst/LegionCore-7.3.5 (AssaultBrokenShore.cpp 500 lignes +
* instance_AssaultBrokenShore.cpp 308 lignes), meme lignee uwow que notre
* fork. Son architecture repose sur des extensions que nous n'avons PAS :
*
* onScenarionNextStep(step) / getScenarionStep() hook de progression
* CRITERIA_TYPE_SCRIPT_EVENT_2 type de critere
* FunctionProcessor ordonnanceur
* GetClosestGraveYard(x,y,z) surcharge d'instance
*
* Aucun de ces quatre elements n'existe ici. La progression est donc
* inversee : au lieu d'etre RAPPELE a chaque etape, le script alimente
* les criteres officiels et le moteur avance de lui-meme, exactement
* comme sur la carte 1460.
*
* CRITERES OFFICIELS, releves dans les DB2 du build 7.3.5.26972
* (ScenarioStep / CriteriaTree / Criteria), et non recopies de la source :
*
* ordre arbre critere type asset montant intitule
* 0 56811 35285 92 56460 1 Into the Fray
* 1 56872 35328 92 56697 3 Vanguard of the Assault
* 2 56874 35329 92 56500 1 Might of the Legion
* 3 57180 35496 92 56779 200 Rifts of Chaos (barre)
* 57185 35495 73 56778 3 ... + les 3 portails
* 4 57183 35497 92 56780 1 The Doomguard's Command
* 5 57293 35551 68 267955 1 Gateway to Ruin
* 6 58303 36178 92 57574 7 Pillar of Fire
* 7 59170 36179 92 57579 1 Mephistroth
*
* type 92 = CRITERIA_TYPE_SEND_EVENT_SCENARIO -> DoSendEventScenario
* type 73 = CRITERIA_TYPE_SEND_EVENT -> UpdateCriteria
* type 68 = CRITERIA_TYPE_USE_GAMEOBJECT -> UpdateCriteria
*
* Les identifiants d'asset de la source coincident avec les officiels :
* verification faite, ils sont conserves.
*
* ECARTS D'API TRAITES, tous constates dans notre code et non supposes :
* DamageTaken(Unit*, uint32&) 2 arguments, pas 3
* OnSpellClick(Unit*, bool&) 2 arguments, pas 1
* EnterEvadeMode(EvadeReason) prend un motif
* GameObjectScript::OnGossipHello(Player*, GameObject*)
* MotionMaster::MovePath(id, repeatable) 2 arguments, pas 4
* PlayerList : GetSource(), pas getSource()
* CreateConversation n'existe QUE sur Player -- la source l'appelle
* sur une creature, cela n'aurait pas compile.
*
* NON PORTE, deliberement : player_scripts_for_start_assault, qui
* sondait CHAQUE joueur a chaque tick pour lui imposer la quete 46730.
* Un donneur de quete en base fait le meme travail sans le cout.
*
* Le nom d'enregistrement « scenario_7.2_broken_shore_intro » est celui
* qu'exige deja instance_template pour la carte 1666. Il contient un
* point, donc ne peut pas passer par la macro RegisterInstanceScript :
* la classe est ecrite a la main pour ne pas avoir a toucher la base.
*/
#include "ScriptMgr.h"
#include "InstanceScript.h"
#include "Scenario.h"
#include "InstanceScenario.h"
#include "ScriptedCreature.h"
#include "MotionMaster.h"
#include "Player.h"
#include "GameObject.h"
#include "GameObjectAI.h"
#include "TemporarySummon.h"
#include "ObjectAccessor.h"
#include "Conversation.h"
enum AssautCreatures
{
// hostiles nommes
NPC_KALGORATH = 116291, // etape 2
NPC_ARGANOTH = 118551, // etape 4, invisible au depart
NPC_MEPHISTROTH = 120746, // etape 7, invisible au depart
NPC_ILLIDAN = 119130,
// allies accompagnant l'assaut
NPC_ALLIE_MAIEV = 119133,
NPC_ALLIE_MAGE_KIRIN_TOR = 118412,
NPC_ALLIE_BLOOMER = 121232,
NPC_ALLIE_CHAMAN = 118966,
NPC_ALLIE_GRAND_PRETRE = 121146,
NPC_ALLIE_ILLIDARI = 118969,
NPC_ALLIE_HAUTE_GARDE = 118945,
NPC_ALLIE_GARDIEN_KIRIN_TOR = 118444,
// vehicule d'arrivee
NPC_CORBEAU_ARCANIQUE = 118517,
// interactifs
NPC_PORTAIL_LEGION = 118558, // etape 3, a fermer x3
NPC_BOMBE_ARCANIQUE = 120743, // etape 6, a poser x7
NPC_INFERNAL_INTRO = 118416,
};
// Elites de la plage : etape 1, trois suffisent.
uint32 const ELITES_PLAGE[] = { 118654, 118453, 118886, 118456 };
// Pietaille comptant pour la barre de progression de l'etape 3.
uint32 const PIETAILLE_RIFTS[] =
{
120511, 118711, 118644, 118647, 118645, 118656, 118655, 118708,
118658, 118563, 118659, 118646, 118650, 118648, 118652, 118653,
118707, 118688, 118457, 118416
};
enum AssautCriteres
{
ASSET_ARRIVEE = 56460, // etape 0, x1
ASSET_ELITE_PLAGE = 56697, // etape 1, x3
ASSET_KALGORATH = 56500, // etape 2, x1
ASSET_PIETAILLE = 56779, // etape 3, barre de 200
ASSET_PORTAIL_FERME = 56778, // etape 3, x3 (type 73)
ASSET_ARGANOTH = 56780, // etape 4, x1
ASSET_PASSERELLE = 267955,// etape 5, x1 (type 68)
ASSET_BOMBE = 57574, // etape 6, x7
ASSET_MEPHISTROTH = 57579, // etape 7, x1
};
enum AssautConversations
{
CONV_ARRIVEE = 4828,
CONV_DEBARQUEMENT = 4829,
CONV_PLAGE_1 = 4831,
CONV_PLAGE_2 = 4832,
CONV_KALGORATH = 4887,
CONV_MEPHISTROTH = 4870,
CONV_FINALE = 4892,
CONV_ENTREE = 4526,
};
enum AssautSorts
{
SPELL_ENTREE_AURA = 240155,
SPELL_ENTREE_INVOCATION = 243303,
SPELL_ENTREE_A_RETIRER = 240188,
SPELL_MONTER_CORBEAU = 52391,
SPELL_PASSERELLE = 236671,
SPELL_FINALE = 243824,
SPELL_INFERNAL_1 = 205266,
SPELL_INFERNAL_2 = 239649,
SPELL_MAGE_REACTION = 46604,
SPELL_BOMBE_VISUEL = 144373,
};
enum AssautChemins
{
CHEMIN_CORBEAU = 11322708,
CHEMIN_ALLIES_PLAGE = 11322705,
CHEMIN_ALLIES_APRES_MAIEV = 11322706,
CHEMIN_ALLIES_ARGANOTH = 11322709,
CHEMIN_ALLIES_DEBUT = 11322707,
};
// Signaux envoyes a l'instance par les scripts de creature et d'objet.
// On passe par SetData plutot que d'appeler DoSendEventScenario depuis
// l'exterieur : l'instance reste seule maitresse de la progression.
enum AssautSignaux
{
DATA_ARRIVEE_ACCOMPLIE = 9201,
DATA_PORTAIL_FERME = 9202,
DATA_BOMBE_POSEE = 9203,
DATA_PASSERELLE_UTILISEE = 9204,
};
enum AssautQuetes
{
QUETE_ASSAUT = 46734,
CREDIT_SPIRE_1 = 116253, // « First Legion Spire destroyed »
CREDIT_SPIRE_2 = 116279, // « Second Legion Spire destroyed »
CREDIT_KHADGAR = 120215, // « Speak to Khadgar »
};
static bool EstDansListe(uint32 entree, uint32 const* liste, size_t taille)
{
for (size_t i = 0; i < taille; ++i)
if (liste[i] == entree)
return true;
return false;
}
class instance_assaut_rivage_brise : public InstanceMapScript
{
public:
instance_assaut_rivage_brise() : InstanceMapScript("scenario_7.2_broken_shore_intro", 1666) { }
struct instance_assaut_rivage_brise_InstanceMapScript : public InstanceScript
{
instance_assaut_rivage_brise_InstanceMapScript(InstanceMap* map) : InstanceScript(map) { }
GuidList allies;
ObjectGuid arganothGuid;
ObjectGuid illidanGuid;
ObjectGuid mephistrothGuid;
uint32 elitesTues = 0;
uint32 portailsFermes = 0;
uint32 bombesPosees = 0;
bool arganothRevele = false;
bool mephistrothRevele = false;
void OnCreatureCreate(Creature* creature) override
{
switch (creature->GetEntry())
{
case NPC_ALLIE_MAIEV:
case NPC_ALLIE_MAGE_KIRIN_TOR:
case NPC_ALLIE_BLOOMER:
case NPC_ALLIE_CHAMAN:
case NPC_ALLIE_GRAND_PRETRE:
case NPC_ALLIE_ILLIDARI:
case NPC_ALLIE_HAUTE_GARDE:
case NPC_ALLIE_GARDIEN_KIRIN_TOR:
allies.push_back(creature->GetGUID());
break;
case NPC_ARGANOTH:
// Reste hors d'atteinte jusqu'a l'etape 4.
creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_NPC | UNIT_FLAG_IMMUNE_TO_PC);
creature->SetVisible(false);
arganothGuid = creature->GetGUID();
break;
case NPC_ILLIDAN:
illidanGuid = creature->GetGUID();
break;
case NPC_MEPHISTROTH:
creature->SetVisible(false);
mephistrothGuid = creature->GetGUID();
break;
default:
break;
}
}
void OnPlayerEnter(Player* player) override
{
// SONDE TEMPORAIRE RIVAGEDBG
TC_LOG_ERROR("misc", "RIVAGEDBG OnPlayerEnter : %s en (%.0f, %.0f, %.0f)",
player ? player->GetName().c_str() : "aucun",
player ? player->GetPositionX() : 0.f,
player ? player->GetPositionY() : 0.f,
player ? player->GetPositionZ() : 0.f);
if (!player)
return;
player->CastSpell(player, SPELL_ENTREE_AURA, true);
// FILET DE SECURITE, temporaire.
// L'etape 0 n'est creditee que par l'arrivee du corbeau. Si
// celui-ci ne vole pas, le scenario reste bloque des la
// premiere etape et le reste devient intestable. On credite
// donc l'arrivee au bout de 20 s si elle ne l'a pas ete.
// A RETIRER une fois le vol repare.
InstanceScript* moi = this;
player->AddDelayedEvent(20000, [moi]() -> void
{
moi->SetData(DATA_ARRIVEE_ACCOMPLIE, 1);
});
player->AddDelayedEvent(5000, [player]() -> void
{
// SONDE TEMPORAIRE RIVAGEDBG
TC_LOG_ERROR("misc", "RIVAGEDBG invocation du corbeau (sort %u)", uint32(SPELL_ENTREE_INVOCATION));
player->CastSpell(player, SPELL_ENTREE_INVOCATION, true);
std::list<Creature*> escorte;
GetCreatureListWithEntryInGrid(escorte, player, NPC_ALLIE_MAGE_KIRIN_TOR, 50.0f);
for (Creature* c : escorte)
c->CastSpell(c, SPELL_ENTREE_INVOCATION, true);
player->AddDelayedEvent(4000, [player]() -> void
{
player->RemoveAura(SPELL_ENTREE_A_RETIRER);
Conversation::CreateConversation(CONV_ENTREE, player, player->GetPosition(), { player->GetGUID() });
});
});
}
// Diffuse une conversation a tous les joueurs de l'instance.
// La source la jouait pour le PREMIER joueur seulement (break dans
// la boucle) : correct en solo, muet pour les autres en groupe.
void ConversationPourTous(uint32 conversationId)
{
Map::PlayerList const& liste = instance->GetPlayers();
for (Map::PlayerList::const_iterator i = liste.begin(); i != liste.end(); ++i)
if (Player* player = i->GetSource())
Conversation::CreateConversation(conversationId, player, player->GetPosition(), { player->GetGUID() });
}
void CritereJoueurs(CriteriaTypes type, uint32 asset)
{
Map::PlayerList const& liste = instance->GetPlayers();
for (Map::PlayerList::const_iterator i = liste.begin(); i != liste.end(); ++i)
if (Player* player = i->GetSource())
player->UpdateCriteria(type, asset);
}
void CreditJoueurs(uint32 creditEntry)
{
Map::PlayerList const& liste = instance->GetPlayers();
for (Map::PlayerList::const_iterator i = liste.begin(); i != liste.end(); ++i)
if (Player* player = i->GetSource())
player->KilledMonsterCredit(creditEntry);
}
void DeplacerAllies(uint32 cheminId)
{
for (ObjectGuid const& guid : allies)
if (Creature* allie = instance->GetCreature(guid))
{
allie->GetMotionMaster()->Clear();
allie->GetMotionMaster()->MovePath(cheminId, false);
}
}
void SetData(uint32 type, uint32 /*data*/) override
{
switch (type)
{
case DATA_ARRIVEE_ACCOMPLIE:
// Etape 0 : le corbeau a depose le joueur sur la plage.
DoSendEventScenario(ASSET_ARRIVEE);
ConversationPourTous(CONV_DEBARQUEMENT);
DeplacerAllies(CHEMIN_ALLIES_PLAGE);
break;
case DATA_PORTAIL_FERME:
// Etape 3 : critere de type 73, trois portails.
CritereJoueurs(CRITERIA_TYPE_SEND_EVENT, ASSET_PORTAIL_FERME);
++portailsFermes;
// Les deux premiers portails valent aussi les credits
// de la quete 45102, qui parle de « First » et
// « Second Legion Spire destroyed ».
if (portailsFermes == 1)
CreditJoueurs(CREDIT_SPIRE_1);
else if (portailsFermes == 2)
CreditJoueurs(CREDIT_SPIRE_2);
break;
case DATA_BOMBE_POSEE:
// Etape 6 : sept bombes.
++bombesPosees;
DoSendEventScenario(ASSET_BOMBE);
break;
case DATA_PASSERELLE_UTILISEE:
// Etape 5 : critere de type 68.
CritereJoueurs(CRITERIA_TYPE_USE_GAMEOBJECT, ASSET_PASSERELLE);
break;
default:
break;
}
}
void OnCreatureKilled(Creature* creature, Player* /*killer*/) override
{
if (!creature)
return;
uint32 const entree = creature->GetEntry();
if (entree == NPC_KALGORATH)
{
// Etape 2. Le credit de quete porte la meme entree : la
// mort suffit, aucun credit supplementaire a accorder.
DoSendEventScenario(ASSET_KALGORATH);
ConversationPourTous(CONV_KALGORATH);
DeplacerAllies(CHEMIN_ALLIES_APRES_MAIEV);
return;
}
if (entree == NPC_ARGANOTH)
{
// Etape 4.
DoSendEventScenario(ASSET_ARGANOTH);
RevelerMephistroth();
return;
}
if (entree == NPC_MEPHISTROTH)
{
// Etape 7, fin du scenario.
DoSendEventScenario(ASSET_MEPHISTROTH);
Finale();
return;
}
if (EstDansListe(entree, ELITES_PLAGE, std::extent<decltype(ELITES_PLAGE)>::value))
{
// Etape 1 : trois elites.
++elitesTues;
DoSendEventScenario(ASSET_ELITE_PLAGE);
if (elitesTues == 1)
ConversationPourTous(CONV_PLAGE_1);
else if (elitesTues == 2)
ConversationPourTous(CONV_PLAGE_2);
else if (elitesTues >= 3)
RevelerArganoth();
return;
}
if (EstDansListe(entree, PIETAILLE_RIFTS, std::extent<decltype(PIETAILLE_RIFTS)>::value))
{
// Etape 3 : barre de progression de 200. La source
// envoyait 2 a 3 unites par mort ; on conserve ce rythme,
// faute de source officielle sur la valeur exacte.
uint32 const pas = urand(2, 3);
for (uint32 i = 0; i < pas; ++i)
DoSendEventScenario(ASSET_PIETAILLE);
return;
}
}
void RevelerArganoth()
{
if (arganothRevele)
return;
arganothRevele = true;
Creature* arganoth = instance->GetCreature(arganothGuid);
if (!arganoth)
return;
arganoth->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_NPC | UNIT_FLAG_IMMUNE_TO_PC);
arganoth->SetVisible(true);
arganoth->GetMotionMaster()->Clear();
if (Creature* illidan = instance->GetCreature(illidanGuid))
{
arganoth->GetMotionMaster()->MovePoint(0, illidan->GetPositionX(), illidan->GetPositionY(), illidan->GetPositionZ());
illidan->AI()->AttackStart(arganoth);
arganoth->AI()->AttackStart(illidan);
}
DeplacerAllies(CHEMIN_ALLIES_ARGANOTH);
}
void RevelerMephistroth()
{
if (mephistrothRevele)
return;
mephistrothRevele = true;
if (Creature* mephistroth = instance->GetCreature(mephistrothGuid))
mephistroth->SetVisible(true);
// CreateConversation n'existe que sur Player dans ce coeur :
// la source l'appelait sur la creature, ce qui n'aurait pas
// compile. On la joue pour les joueurs presents.
ConversationPourTous(CONV_MEPHISTROTH);
}
void Finale()
{
Map::PlayerList const& liste = instance->GetPlayers();
for (Map::PlayerList::const_iterator i = liste.begin(); i != liste.end(); ++i)
{
Player* player = i->GetSource();
if (!player)
continue;
player->AddDelayedEvent(4000, [player]() -> void
{
Conversation::CreateConversation(CONV_FINALE, player, player->GetPosition(), { player->GetGUID() });
player->CastSpell(player, SPELL_FINALE, true);
player->KilledMonsterCredit(CREDIT_KHADGAR);
});
}
}
};
InstanceScript* GetInstanceScript(InstanceMap* map) const override
{
return new instance_assaut_rivage_brise_InstanceMapScript(map);
}
};
// Les allies qui accompagnent l'assaut. Chacun a sa rotation propre,
// reprise telle quelle de la source : ce sont des donnees, pas des choix.
class npc_assaut_allie : public CreatureScript
{
public:
npc_assaut_allie() : CreatureScript("npc_assaut_allie") { }
struct npc_assaut_allieAI : public ScriptedAI
{
npc_assaut_allieAI(Creature* creature) : ScriptedAI(creature)
{
instance = me->GetInstanceScript();
}
InstanceScript* instance;
EventMap events;
void Reset() override
{
events.Reset();
}
void EnterCombat(Unit* /*who*/) override
{
switch (me->GetEntry())
{
case NPC_ALLIE_MAIEV:
DoCast(225184);
events.RescheduleEvent(1, 6000);
break;
case NPC_ALLIE_MAGE_KIRIN_TOR:
events.RescheduleEvent(2, 2500);
events.RescheduleEvent(3, 18000);
break;
case NPC_ALLIE_BLOOMER:
DoCast(79833);
events.RescheduleEvent(4, 2300);
events.RescheduleEvent(5, 14000);
break;
case NPC_ALLIE_CHAMAN:
events.RescheduleEvent(6, 4000);
events.RescheduleEvent(7, 35000);
events.RescheduleEvent(8, 11000);
events.RescheduleEvent(9, 14000);
break;
case NPC_ALLIE_GRAND_PRETRE:
events.RescheduleEvent(10, 3000);
events.RescheduleEvent(11, 13000);
events.RescheduleEvent(12, 15000);
break;
case NPC_ALLIE_ILLIDARI:
events.RescheduleEvent(13, 7000);
events.RescheduleEvent(14, 27000);
events.RescheduleEvent(15, 21000);
events.RescheduleEvent(16, 31000);
break;
case NPC_ALLIE_HAUTE_GARDE:
events.RescheduleEvent(17, 4000);
events.RescheduleEvent(18, 9000);
events.RescheduleEvent(19, 39000);
events.RescheduleEvent(20, 31000);
break;
case NPC_ALLIE_GARDIEN_KIRIN_TOR:
events.RescheduleEvent(21, 3000);
events.RescheduleEvent(22, 20000);
break;
default:
break;
}
}
// Les allies doivent tenir jusqu'au bout de l'assaut : les degats
// recus sont divises d'autant plus fort qu'ils sont bas en vie.
// Formule reprise de la source ; le diviseur vaut au minimum 1,
// donc pas de division par zero.
void DamageTaken(Unit* /*attacker*/, uint32& damage) override
{
uint32 const diviseur = 11 - uint32(me->GetHealthPct() / 10.0f);
damage /= std::max<uint32>(1, diviseur);
}
void MovementInform(uint32 type, uint32 /*id*/) override
{
me->SetHomePosition(me->GetPositionX(), me->GetPositionY(), me->GetPositionZ(), me->GetOrientation());
if (type == WAYPOINT_MOTION_TYPE)
ChercherUnAdversaire();
}
// Cherche un hostile a portee et l'engage.
bool ChercherUnAdversaire()
{
std::list<Creature*> hostiles;
for (uint32 entree : PIETAILLE_RIFTS)
GetCreatureListWithEntryInGrid(hostiles, me, entree, 40.0f);
for (uint32 entree : ELITES_PLAGE)
GetCreatureListWithEntryInGrid(hostiles, me, entree, 40.0f);
for (Creature* cible : hostiles)
if (cible->IsAlive())
{
AttackStart(cible);
return true;
}
return false;
}
void UpdateAI(uint32 diff) override
{
if (me->HasUnitState(UNIT_STATE_STUNNED))
return;
if (!UpdateVictim())
return;
events.Update(diff);
if (me->HasUnitState(UNIT_STATE_CASTING))
return;
if (uint32 eventId = events.ExecuteEvent())
{
struct Rotation { uint32 evenement; uint32 sort; uint32 rappel; };
static Rotation const rotations[] =
{
{ 1, 226063, 6000 },
{ 2, 183108, 2500 }, { 3, 183075, 18000 },
{ 4, 182856, 2300 }, { 5, 182854, 14000 },
{ 6, 190330, 4000 }, { 7, 189804, 35000 },
{ 8, 190332, 11000 }, { 9, 190331, 14000 },
{ 10, 183555, 3000 }, { 11, 183553, 13000 }, { 12, 183549, 15000 },
{ 13, 222971, 7000 }, { 14, 223061, 27000 }, { 15, 223109, 21000 }, { 16, 223903, 31000 },
{ 17, 183894, 4000 }, { 18, 183897, 9000 }, { 19, 183898, 39000 }, { 20, 190010, 31000 },
{ 21, 183108, 3000 }, { 22, 13730, 20000 },
};
for (Rotation const& r : rotations)
if (r.evenement == eventId)
{
DoCast(r.sort);
events.RescheduleEvent(r.evenement, r.rappel);
break;
}
}
DoMeleeAttackIfReady();
}
};
CreatureAI* GetAI(Creature* creature) const override
{
return new npc_assaut_allieAI(creature);
}
};
// 118517 : le corbeau arcanique qui depose le joueur sur la plage.
// Sa fin de trajet vaut l'etape 0 du scenario.
class npc_assaut_corbeau_arcanique : public CreatureScript
{
public:
npc_assaut_corbeau_arcanique() : CreatureScript("npc_assaut_corbeau_arcanique") { }
struct npc_assaut_corbeau_arcaniqueAI : public ScriptedAI
{
npc_assaut_corbeau_arcaniqueAI(Creature* creature) : ScriptedAI(creature) { }
void IsSummonedBy(Unit* summoner) override
{
// SONDE TEMPORAIRE RIVAGEDBG
TC_LOG_ERROR("misc", "RIVAGEDBG corbeau invoque par %s en (%.0f, %.0f, %.0f)",
summoner ? summoner->GetName().c_str() : "personne",
me->GetPositionX(), me->GetPositionY(), me->GetPositionZ());
SetFlyMode(true);
if (summoner)
summoner->CastSpell(me, SPELL_MONTER_CORBEAU, true);
}
void PassengerBoarded(Unit* passager, int8 /*seatId*/, bool apply) override
{
// SONDE TEMPORAIRE RIVAGEDBG
TC_LOG_ERROR("misc", "RIVAGEDBG passager %s %s le corbeau",
passager ? passager->GetName().c_str() : "inconnu", apply ? "monte sur" : "descend de");
Creature* moi = me;
me->AddDelayedEvent(4000, [moi]() -> void
{
moi->GetMotionMaster()->MovePath(CHEMIN_CORBEAU, false);
});
}
void MovementInform(uint32 moveType, uint32 pointId) override
{
if (moveType != WAYPOINT_MOTION_TYPE || pointId != 20)
return;
Unit* proprietaire = me->GetAnyOwner();
if (proprietaire && proprietaire->IsPlayer())
{
Conversation::CreateConversation(CONV_ARRIVEE, proprietaire, proprietaire->GetPosition(), { proprietaire->GetGUID() });
if (InstanceScript* instance = me->GetInstanceScript())
instance->SetData(DATA_ARRIVEE_ACCOMPLIE, 1);
// Un infernal s'ecrase a l'arrivee, et un mage du Kirin Tor
// y reagit : mise en scene reprise de la source.
if (Creature* infernal = me->SummonCreature(NPC_INFERNAL_INTRO, me->GetPositionX() + 5.0f, me->GetPositionY() + 5.0f, me->GetPositionZ()))
{
infernal->CastSpell(infernal, SPELL_INFERNAL_1, true);
infernal->CastSpell(infernal, SPELL_INFERNAL_2, true);
if (Creature* mage = infernal->FindNearestCreature(NPC_ALLIE_MAGE_KIRIN_TOR, 30.0f, true))
{
mage->CastSpell(mage, SPELL_MAGE_REACTION, true);
mage->AI()->Talk(0);
}
}
}
me->DespawnOrUnsummon(500);
}
};
CreatureAI* GetAI(Creature* creature) const override
{
return new npc_assaut_corbeau_arcaniqueAI(creature);
}
};
// 118558 : portail de la Legion a fermer (etape 3, trois fois).
// 120743 : bombe arcanique a poser (etape 6, sept fois).
// Les deux s'activent au clic de sort.
class npc_assaut_interactif : public CreatureScript
{
public:
npc_assaut_interactif() : CreatureScript("npc_assaut_interactif") { }
struct npc_assaut_interactifAI : public ScriptedAI
{
npc_assaut_interactifAI(Creature* creature) : ScriptedAI(creature)
{
me->SetReactState(REACT_PASSIVE);
me->SetFlag64(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_SPELLCLICK);
}
void MoveInLineOfSight(Unit* /*who*/) override { }
void UpdateAI(uint32 /*diff*/) override { }
void OnSpellClick(Unit* clicker, bool& /*result*/) override
{
if (!clicker || !clicker->IsPlayer())
return;
// Un seul usage : on retire le drapeau avant tout traitement.
me->RemoveFlag64(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_SPELLCLICK);
InstanceScript* instance = me->GetInstanceScript();
if (!instance)
return;
if (me->GetEntry() == NPC_PORTAIL_LEGION)
instance->SetData(DATA_PORTAIL_FERME, 1);
else if (me->GetEntry() == NPC_BOMBE_ARCANIQUE)
{
instance->SetData(DATA_BOMBE_POSEE, 1);
me->RemoveAura(SPELL_BOMBE_VISUEL);
}
}
};
CreatureAI* GetAI(Creature* creature) const override
{
return new npc_assaut_interactifAI(creature);
}
};
// 267955 : la passerelle demoniaque menant au vaisseau amiral (etape 5).
class go_assaut_passerelle_demoniaque : public GameObjectScript
{
public:
go_assaut_passerelle_demoniaque() : GameObjectScript("go_assaut_passerelle_demoniaque") { }
bool OnGossipHello(Player* player, GameObject* go) override
{
if (!player || !go)
return false;
InstanceScript* instance = go->GetInstanceScript();
if (!instance)
return false;
// Tout le groupe franchit la passerelle ensemble.
Map::PlayerList const& liste = go->GetMap()->GetPlayers();
for (Map::PlayerList::const_iterator i = liste.begin(); i != liste.end(); ++i)
if (Player* p = i->GetSource())
p->CastSpell(p, SPELL_PASSERELLE, true);
instance->SetData(DATA_PASSERELLE_UTILISEE, 1);
return true;
}
};
void AddSC_scenario_assaut_rivage_brise()
{
new instance_assaut_rivage_brise();
new npc_assaut_allie();
new npc_assaut_corbeau_arcanique();
new npc_assaut_interactif();
new go_assaut_passerelle_demoniaque();
}
@@ -17,6 +17,9 @@
#include "GameObject.h"
#include "ObjectMgr.h"
#include "TaskScheduler.h"
#include "MercenaryMgr.h"
#include "Chat.h"
#include "Group.h"
enum BrokenShoreData
{
@@ -105,9 +108,98 @@ enum BrokenShoreMisc
GO_SPIRE_OF_WOE = 240194,
DATA_SPIRE_USED = 9001, // signal envoye par le script d'objet
// ==============================================================
// Criteres officiels des etapes 2 a 4, releves dans ScenarioStep,
// CriteriaTree et Criteria du build 7.3.5.26972 :
// etape 2 arbre 43554 critere 30883 type 92 asset 45131 x1
// etape 3 arbre 43589 critere 28017 type 92 asset 45228 x1
// etape 4 arbre 43415 critere 27940 type 92 asset 45288 x4
//
// L'ancre du portail a ete identifiee sur la VIDEO de la bataille
// fournie par l'exploitant, et non deduite : l'objectif y affiche
// « 0/4 Ancres blindees detruites », puis 2/4, puis 3/4. Il s'agit
// donc de 101667 « Shielded Anchor », dont la carte porte QUINZE
// exemplaires deja poses autour de (1107, 2061) -- et non des deux
// ancres dimensionnelles 90637 que le script invoquait lui-meme.
// ==============================================================
NPC_SHIELDED_ANCHOR = 101667,
EVENT_COMMANDER_SLAIN = 45131,
EVENT_LEADER_FOUND = 45228,
EVENT_ANCHOR_DESTROYED = 45288,
// ==============================================================
// Etape 6, « Raser la cite noire ». Son arbre 42770 porte
// l'operateur 9, CRITERIA_TREE_OPERATOR_SUM_CHILDREN_WEIGHT : la
// barre vaut 300 points et chaque enfant y contribue selon SON
// poids, releve dans les DB2 du build 7.3.5.26972 :
//
// asset 44384 poids 1 -> 0,33 % par activation
// asset 53062 poids 2 -> 0,67 %
// asset 53063 poids 5 -> 1,67 %
// asset 53064 poids 10 -> 3,33 %
//
// SIGNALE EN JEU : « la phase 6 ne progresse pas en tuant, la
// progression reste a 0 % ». Le script n'envoyait aucun de ces
// quatre evenements : il comptait dix morts dans son coin et
// forcait le passage, laissant la barre morte.
//
// DEDUCTION ASSUMEE, ET NON DONNEE : rien n'indique quel ennemi
// porte quel poids. La cite ne contient que DEUX categories --
// 94 demons ordinaires et 17 elites -- la ou les poids en
// supposent quatre. On attribue donc le poids 2 aux ordinaires et
// le poids 5 aux elites, ce qui fait progresser la barre a
// proportion de la difficulte. Les poids 1 et 10 restent
// inemployes faute de savoir ce qu'ils designent.
// ==============================================================
// AJUSTE SUR OBSERVATION EN JEU : « la progression de p6 est trop
// lente ». Avec les poids 2 et 5, les 94 demons ordinaires et 17
// elites de la cite plafonnaient a 273 points sur 300 -- d'ou la
// lenteur, et d'ou les vagues de renfort que j'avais ajoutees pour
// compenser, puis retirees. On monte d'un cran : une soixantaine
// d'ennemis suffit desormais, sans renfort artificiel.
EVENT_CITY_TRASH = 53063, // poids 5
EVENT_CITY_ELITE = 53064, // poids 10
// Etapes 7 a 9, criteres releves dans les memes DB2 :
// etape 7 arbre 42772 critere 29715 asset 50027 x1
// etape 8 arbre 43765 critere 28055 asset 44669 x1
// etape 9 arbre 47225 critere 29714 asset 44826 x1
// Le quatrieme poids de la barre de l'etape 6, longtemps inemploye.
// SIGNALE EN JEU : « les gameobject Legion Cage ne comptent pas »,
// puis « oui la barre bouge de 1 % » -- les cages alimentent donc
// bien la barre. Le poids 1 est le seul des quatre qui restait
// libre, et il correspond a une action mineure : 1 point sur 300,
// soit 0,33 %, ce qui s'affiche comme 1 % des la deuxieme cage.
EVENT_CITY_CAGE = 44384,
GO_LEGION_CAGE = 240535,
GO_LEGION_CAGE_2 = 248819,
DATA_CAGE_OPENED = 9002,
// ==============================================================
// SylvaniaCore : les protagonistes de la crevasse SONT POSES sur la
// carte, sous d'autres entrees que celles du script :
// 91951 Highlord Tirion Fordring (1495, 1751)
// 94276 Gul'dan (1530, 1742)
// 90544 Krosus (1481, 1716)
// 90705 Dread Commander Arganoth ( 613, 2085)
//
// SIGNALE EN JEU : « tu ne les as pas implementes au Tirion et
// Krosus qu'il y avait de base sur la map, tu en as ajoute ».
// Meme faute que pour Varian : le script invoquait des sosies aux
// entrees 90367 et 90413, qui ne figurent nulle part sur la carte.
// On emploie desormais ceux de la base, retenus a leur creation.
// ==============================================================
NPC_TIRION_POSE = 91951,
NPC_GULDAN_POSE = 94276,
EVENT_TIRION_REACHED = 50027,
EVENT_KROSUS_SLAIN = 44669,
EVENT_GULDAN_STOPPED = 44826,
KILLS_CITY = 10,
KILLS_FINALE = 8,
ANCHORS_PORTAL = 2,
ANCHORS_PORTAL = 4, // etait 2, valeur inventee
};
struct FactionAnchors
@@ -199,11 +291,73 @@ struct scenario_broken_shore_intro : public InstanceScript
// objectif « embarquement » (Alliance) : credite aussi ici au cas ou
player->KilledMonsterCredit(NPC_CREDIT_SHIP);
// =============================================================
// ESCORTE_MERCENAIRE
//
// DEMANDE : « cree le systeme de groupe avec playerbot pour le
// scenario, avec un groupe complet heal tank dps », en reprenant
// le principe des mercenaires mais declenche a l entree.
//
// Le scenario officiel se joue en groupe, constitue par la file
// d attente. Faute de ce systeme, on offre une escorte : un
// protecteur, un guerisseur et deux combattants, gratuitement.
//
// Le contrat reste celui des mercenaires -- rupture au premier
// depart du groupe -- mais sans prelevement : le jeu la fournit,
// le joueur ne l a pas louee.
//
// On ne complete que ce qui manque : un joueur deja accompagne
// garde ses compagnons.
// =============================================================
if (sMercenaryMgr->IsEnabled())
{
uint32 place = MERCENARY_GROUP_SIZE - 1;
if (Group* groupe = player->GetGroup())
place = (groupe->GetMembersCount() < MERCENARY_GROUP_SIZE)
? MERCENARY_GROUP_SIZE - groupe->GetMembersCount() : 0;
static uint8 const composition[] = { ROLE_TANK, ROLE_HEALER, ROLE_DAMAGE, ROLE_DAMAGE };
// SIGNALE EN JEU : « une se superpose a la meme position ».
// On espace les arrivees d'une seconde et demie : le module
// pose chaque mercenaire aupres de son employeur, et deux
// invocations simultanees se retrouvent au meme point.
uint8 recrutes = 0;
for (uint8 i = 0; i < 4 && recrutes < place; ++i)
{
uint8 const role = composition[i];
Player* employeur = player;
scheduler.Schedule(Milliseconds(1500 * (i + 1)), [employeur, role](TaskContext)
{
sMercenaryMgr->Summon(employeur, role, nullptr, true);
});
++recrutes;
}
if (recrutes)
ChatHandler(player->GetSession()).PSendSysMessage(
"Une escorte de %u combattants se joint a vous pour l'assaut.", recrutes);
}
if (!introDone)
{
introDone = true;
team = player->GetTeamId();
StartIntro();
// =====================================================
// SIGNALE EN JEU : « Varian a un dialogue audio au
// lancement de la campagne, il ne se declenche pas ».
//
// StartIntro etait appele DANS OnPlayerEnter, c'est-a-dire
// pendant l'ajout du joueur a la carte. Le cri partait
// alors que le client chargeait encore la zone : il ne le
// recevait jamais. On laisse quatre secondes au client pour
// se poser avant d'ouvrir la scene.
// =====================================================
scheduler.Schedule(Seconds(4), [this](TaskContext /*c*/)
{
StartIntro();
});
}
}
@@ -213,6 +367,55 @@ struct scenario_broken_shore_intro : public InstanceScript
scheduler.Update(diff);
}
// =================================================================
// SylvaniaCore : retenir le chef POSE SUR LA CARTE.
//
// SIGNALE EN JEU : « je suis en phase 4, je suis juste a cote de
// Varian et rien ne se valide ».
//
// La carte 1460 porte deja les vrais protagonistes -- Varian en
// (1120, 2484), Vol'jin en (568, 1887), Sylvanas et Jaina -- parmi
// les 757 creatures transposees. Or le script en invoquait une
// SECONDE copie sur la plage et ne surveillait que celle-la. Le
// joueur se tenait donc devant le vrai Varian pendant que l'etape
// guettait un sosie ailleurs, ou deja disparu.
//
// Le spawnId distingue les deux sans ambiguite : une creature issue
// de la base en porte un, une invocation non.
// =================================================================
void OnCreatureCreate(Creature* creature) override
{
InstanceScript::OnCreatureCreate(creature);
if (!creature || !creature->GetSpawnId())
return;
switch (creature->GetEntry())
{
case NPC_KING_VARIAN: placedVarianGUID = creature->GetGUID(); break;
case NPC_VOLJIN: placedVoljinGUID = creature->GetGUID(); break;
case NPC_JAINA: jainaGUID = creature->GetGUID(); break;
case NPC_SYLVANAS: sylvanasGUID = creature->GetGUID(); break;
case NPC_GENN: gennGUID = creature->GetGUID(); break;
case NPC_THRALL: thrallGUID = creature->GetGUID(); break;
case NPC_TIRION_POSE: tirionGUID = creature->GetGUID(); break;
case NPC_GULDAN_POSE: guldanGUID = creature->GetGUID(); break;
case NPC_KROSUS: krosusGUID = creature->GetGUID(); break;
case NPC_ARGANOTH: arganothGUID = creature->GetGUID(); break;
default: break;
}
}
// Le chef a rejoindre : celui de la carte s'il existe, sinon la
// copie invoquee par le script.
Creature* FindLeader() const
{
ObjectGuid const pose = (team == TEAM_HORDE) ? placedVoljinGUID : placedVarianGUID;
if (Creature* leader = instance->GetCreature(pose))
return leader;
return instance->GetCreature(leaderGUID);
}
void CompleteStep()
{
if (Scenario* scenario = instance->GetInstanceScenario())
@@ -272,38 +475,81 @@ struct scenario_broken_shore_intro : public InstanceScript
void StartIntro()
{
FactionAnchors const& a = Anchors();
// =============================================================
// CAST DEJA POSE SUR LA CARTE : plus aucune invocation.
//
// SIGNALE EN JEU : « je n'avais pas remarque mais a cette
// position il y a un 2e Genn Grisetete et une 2e Jaina ».
//
// Troisieme fois que ce defaut se manifeste -- apres Varian,
// puis Tirion et Krosus. La carte porte TOUTE la distribution,
// a l'endroit qui lui revient :
// Genn Grisetete (487, 2052) a 49 m du debarquement
// Jaina (491, 2047) a 56 m
// Vol'jin, Thrall (568-572, 1882-1887) plage Horde
// Baine, Sylvanas (992-1000, 1874-1881)
// Mekkatorque, Varian (1117-1120, 2471-2484)
//
// Le script en fabriquait des copies au point d'ancrage, d'ou
// les doublons. On retient les vraies dans OnCreatureCreate et
// on ne convoque plus personne.
//
// Le chef de faction n'ouvre plus la scene : sur la plage, ce
// sont Genn et Jaina cote Alliance, Vol'jin cote Horde. Varian
// est ailleurs -- c'est tout l'objet de l'etape « Trouver
// Varian » d'aller le chercher.
// =============================================================
leaderGUID = (team == TEAM_HORDE) ? placedVoljinGUID : placedVarianGUID;
// debarquement : chef de faction + escorte
if (Creature* leader = Summon(LeaderEntry(), a.beach))
{
leaderGUID = leader->GetGUID();
leader->AI()->Talk(0);
}
if (team == TEAM_HORDE)
{
if (Creature* sylvanas = SummonAt(NPC_SYLVANAS, a.beach, 4.0f, 4.0f))
sylvanasGUID = sylvanas->GetGUID();
SummonAt(NPC_BAINE, a.beach, -4.0f, 4.0f);
SummonAt(NPC_THRALL, a.beach, 4.0f, -4.0f);
}
else
{
if (Creature* jaina = SummonAt(NPC_JAINA, a.beach, 4.0f, 4.0f))
jainaGUID = jaina->GetGUID();
SummonAt(NPC_GENN, a.beach, -4.0f, 4.0f);
SummonAt(NPC_MEKKATORQUE, a.beach, 4.0f, -4.0f);
}
for (uint8 i = 0; i < 4; ++i)
SummonAt(TroopEntry(), a.beach, -8.0f + i * 5.0f, -8.0f);
if (Creature* orateur = instance->GetCreature(team == TEAM_HORDE ? placedVoljinGUID : gennGUID))
orateur->AI()->Talk(0);
// etape 0 « The Broken Shore » : courte mise en scene puis assaut
scheduler.Schedule(Seconds(12), [this](TaskContext /*context*/)
// =============================================================
// ATTEINDRE_LA_PLAGE
//
// L'etape 1 s'intitule « Rendez-vous au rivage Brise » : elle se
// joue depuis le pont du navire, et ne s'acheve qu'une fois le
// joueur debarque. Une minuterie de douze secondes la validait
// sans lui -- et comme le client mettait ce temps a charger la
// zone, il se retrouvait deja en phase 2 a son arrivee.
//
// On attend desormais qu'il pose le pied sur le sable, a moins
// de 40 metres du point de debarquement.
// =============================================================
scheduler.Schedule(Seconds(3), [this](TaskContext context)
{
if (stage != STAGE_INTRO)
return;
// SIGNALE EN JEU : « je reste bloque en p1 ». Le pont du
// navire est a 52 metres du point de debarquement, donc hors
// du cercle de 40 que j'exigeais : selon l'endroit ou le
// joueur descendait, il n'y entrait jamais. Porte a 90, ce
// qui couvre toute la greve devant le navire.
bool debarque = false;
Position const& plage = Anchors().beach;
DoOnPlayers([&debarque, &plage](Player* player)
{
if (player->GetExactDist2d(plage.GetPositionX(), plage.GetPositionY()) < 90.0f)
debarque = true;
});
if (!debarque)
{
context.Repeat(Seconds(3));
return;
}
// PLUS AUCUNE VAGUE. Le script a ete ecrit quand la carte
// etait VIDE : il fabriquait ses propres ennemis partout.
// Elle porte desormais 748 creatures posees, et ces
// invocations faisaient double emploi.
//
// SIGNALE EN JEU : « il y a toujours ces vagues de demons
// qui m'attaquent, RETIRE-LES ». Elles rendaient le combat
// interminable et repoussaient sans fin.
stage = STAGE_STORM_BEACH;
CompleteStep();
SummonWave(Anchors().beach, 6);
SummonWave(Anchors().commander, 6);
});
}
@@ -320,7 +566,7 @@ struct scenario_broken_shore_intro : public InstanceScript
case NPC_FELBLADE_DESTROYER:
case NPC_EREDAR_SUMMONER:
case NPC_INFERNAL_DESTROYER:
OnDemonDied();
OnDemonDied(creature);
break;
// Les treize Seigneurs gangrebois places sur la carte. L'etape
// en exige trois ; on les enumere explicitement plutot que de
@@ -340,25 +586,29 @@ struct scenario_broken_shore_intro : public InstanceScript
if (stage == STAGE_COMMANDER)
{
creature->AI()->Talk(1);
DoSendEventScenario(EVENT_COMMANDER_SLAIN);
stage = STAGE_FIND_LEADER;
CompleteStep();
StartFindLeader();
}
break;
case NPC_DIMENSIONAL_ANCHOR:
if (stage == STAGE_PORTAL && ++anchorsDown >= ANCHORS_PORTAL)
case NPC_SHIELDED_ANCHOR:
if (stage != STAGE_PORTAL)
break;
// Une activation par ancre : le critere en exige quatre.
DoSendEventScenario(EVENT_ANCHOR_DESTROYED);
if (++anchorsDown >= ANCHORS_PORTAL)
{
// La cite est deja peuplee : rien a invoquer.
stage = STAGE_RAZE_CITY;
CompleteStep();
SummonWave(Anchors().city, 5);
SummonWave(Anchors().city, 5);
}
break;
case NPC_KROSUS:
if (stage == STAGE_KROSUS)
{
DoSendEventScenario(EVENT_KROSUS_SLAIN);
stage = STAGE_STOP_GULDAN;
CompleteStep();
StartFinale();
}
break;
@@ -383,7 +633,7 @@ struct scenario_broken_shore_intro : public InstanceScript
// arrivent jamais ici : ils ont leurs propres criteres.
// =====================================================
if (creature->GetCreatureTemplate()->type == CREATURE_TYPE_DEMON)
OnDemonDied();
OnDemonDied(creature);
break;
}
}
@@ -393,6 +643,14 @@ struct scenario_broken_shore_intro : public InstanceScript
// d'instance ne rapporte cette utilisation.
void SetData(uint32 type, uint32 /*data*/) override
{
if (type == DATA_CAGE_OPENED)
{
// Une cage ne vaut que pendant l'assaut de la cite.
if (stage == STAGE_RAZE_CITY)
DoSendEventScenario(EVENT_CITY_CAGE);
return;
}
if (type != DATA_SPIRE_USED || stage != STAGE_STORM_BEACH)
return;
@@ -411,17 +669,24 @@ struct scenario_broken_shore_intro : public InstanceScript
if (beachKills < KILLS_BEACH || felLordKills < FEL_LORDS_BEACH || spiresDown < SPIRES_BEACH)
return;
// Les trois criteres officiels ont ete alimentes a chaque mort :
// le moteur acheve l'etape de lui-meme. Un CompleteStep() ici
// ferait avancer une SECONDE fois -- c'est la cause du saut
// d'etapes signale en jeu (« j'ai passe ma phase 2 et ca m'a
// switche jusqu'a la 5 sans rien faire »).
stage = STAGE_COMMANDER;
CompleteStep();
if (Creature* arganoth = Summon(NPC_ARGANOTH, Anchors().commander))
// Arganoth est POSE sur la carte en (613, 2085) : quatrieme
// occurrence du meme defaut, apres Varian, Tirion, Krosus et la
// distribution de la plage. On emploie celui de la base.
if (Creature* arganoth = instance->GetCreature(arganothGUID))
{
arganoth->AI()->Talk(0);
arganoth->SetInCombatWithZone();
}
}
void OnDemonDied()
void OnDemonDied(Creature* mort)
{
switch (stage)
{
@@ -431,29 +696,87 @@ struct scenario_broken_shore_intro : public InstanceScript
TryFinishBeach();
break;
case STAGE_RAZE_CITY:
if (++cityKills >= KILLS_CITY)
{
// La barre officielle avance selon la valeur de l'ennemi.
bool const elite = mort && mort->GetCreatureTemplate()->rank > 0;
DoSendEventScenario(elite ? EVENT_CITY_ELITE : EVENT_CITY_TRASH);
++cityKills;
// Des vagues continuent d'affluer tant que la cite tient :
// sans cela la barre ne pourrait pas se remplir, la zone ne
// comptant pas assez de defenseurs pour ses 300 points.
//
// SIGNALE EN JEU : « en phase 6 j'ai des invocations de
// demon sur ma tronche ». Elles naissaient au point de
// ralliement, c'est-a-dire au milieu du combat. Elles
// arrivent desormais de la peripherie et chargent.
// VAGUES SUPPRIMEES. SIGNALE EN JEU : « ces invocations de
// demon, il faut arreter ca, a chaque fois que je me bats
// ils reviennent en vague, c'est affreux ». Elles etaient
// une addition de ma part pour permettre a la barre
// d'atteindre 300 points -- un pansement sur une deduction
// incertaine, qui rendait le combat interminable. Si la
// barre plafonne, c'est la correspondance des poids qu'il
// faudra revoir, pas le nombre d'ennemis.
// FILET DE SECURITE, pas un mecanisme. Si la barre restait
// bloquee pour une raison qui nous echappe, le joueur ne
// doit pas rester prisonnier de l'etape.
if (cityKills >= 90)
{
stage = STAGE_HIGHLORD;
CompleteStep();
StartHighlord();
}
break;
}
case STAGE_STOP_GULDAN:
if (++finaleKills >= KILLS_FINALE)
FinishScenario();
// On compte encore, pour memoire, mais la fin est
// desormais reglee par la sequence de Gul'dan.
++finaleKills;
break;
default:
break;
}
}
// Renforts de la cite : ils surgissent en peripherie, a une
// cinquantaine de metres, sur un cercle dont l'orientation change a
// chaque vague -- puis ils chargent. Aucun demon ne se materialise
// dans le dos du joueur.
void SummonWaveLoin()
{
static uint32 const demons[5] =
{ NPC_CHAOS_MINION, NPC_FELBLOOD_PACKHOUND, NPC_FELBLADE_DESTROYER,
NPC_EREDAR_SUMMONER, NPC_INFERNAL_DESTROYER };
FactionAnchors const& a = Anchors();
float const depart = frand(0.0f, float(M_PI) * 2.0f);
for (uint8 i = 0; i < 5; ++i)
{
float const angle = depart + float(i) * (float(M_PI) * 2.0f / 5.0f);
float const rayon = frand(45.0f, 60.0f);
if (TempSummon* demon = SummonAt(demons[i], a.city,
std::cos(angle) * rayon,
std::sin(angle) * rayon))
{
demon->SetReactState(REACT_AGGRESSIVE);
demon->SetInCombatWithZone();
}
}
}
void StartFindLeader()
{
FactionAnchors const& a = Anchors();
// le chef et son etat-major se replacent au point de ralliement (la Cite noire)
if (Creature* leader = instance->GetCreature(leaderGUID))
leader->NearTeleportTo(a.city.GetPositionX(), a.city.GetPositionY(), a.city.GetPositionZ(), a.city.GetOrientation());
// On ne DEPLACE plus le chef. L'etape s'intitule « Trouver
// Varian » : le joueur doit aller a lui, la ou la carte le pose.
// Le teleporter au point de ralliement revenait a le mettre
// sous les pieds du joueur, et la detection de proximite se
// declenchait alors dans la seconde.
SummonAt(NPC_KHADGAR, a.city, 4.0f, 3.0f);
for (uint8 i = 0; i < 3; ++i)
SummonAt(TroopEntry(), a.city, -6.0f + i * 6.0f, -5.0f);
@@ -464,7 +787,7 @@ struct scenario_broken_shore_intro : public InstanceScript
if (stage != STAGE_FIND_LEADER)
return;
bool found = false;
Creature* leader = instance->GetCreature(leaderGUID);
Creature* leader = FindLeader();
if (leader)
{
DoOnPlayers([&found, leader](Player* player)
@@ -475,8 +798,8 @@ struct scenario_broken_shore_intro : public InstanceScript
}
if (found)
{
DoSendEventScenario(EVENT_LEADER_FOUND);
stage = STAGE_PORTAL;
CompleteStep();
if (Creature* second = instance->GetCreature(team == TEAM_HORDE ? sylvanasGUID : jainaGUID))
second->AI()->Talk(0);
StartPortal();
@@ -489,8 +812,10 @@ struct scenario_broken_shore_intro : public InstanceScript
void StartPortal()
{
FactionAnchors const& a = Anchors();
SummonAt(NPC_DIMENSIONAL_ANCHOR, a.portal, -8.0f, 0.0f);
SummonAt(NPC_DIMENSIONAL_ANCHOR, a.portal, 8.0f, 0.0f);
// Les ancres blindees (101667) sont DEJA posees sur la carte,
// quinze exemplaires autour de (1107, 2061). Rien a invoquer :
// le script en fabriquait deux fausses, d'une autre entree, que
// le critere officiel ne reconnaissait pas.
SummonAt(NPC_EREDAR_SUMMONER, a.portal, 0.0f, 6.0f);
SummonAt(NPC_CHAOS_MINION, a.portal, -5.0f, 8.0f);
SummonAt(NPC_CHAOS_MINION, a.portal, 5.0f, 8.0f);
@@ -498,45 +823,223 @@ struct scenario_broken_shore_intro : public InstanceScript
void StartHighlord()
{
FactionAnchors const& a = Anchors();
if (Creature* tirion = Summon(NPC_TIRION, a.crevasse))
{
tirion->AI()->Talk(0);
// =============================================================
// SylvaniaCore : l'etape s'intitule « Atteindre Tirion ».
//
// SIGNALE EN JEU : « la 7 c'est Tirion, il est passe
// automatiquement et ca me passe en 8 ».
//
// La minuterie de douze secondes validait l'etape SANS LE
// JOUEUR, et Tirion disparaissait au bout de vingt secondes --
// impossible de l'atteindre meme en courant. Meme defaut que
// « Trouver Varian » : le script decidait a la place du joueur.
//
// On attend desormais qu'un joueur le rejoigne, et on alimente
// le critere officiel 50027. Tirion reste en place : il agonise
// dans la crevasse, il n'a aucune raison de s'evaporer.
// =============================================================
// Tirion est deja sur la carte, agonisant dans la crevasse.
if (Creature* tirion = instance->GetCreature(tirionGUID))
tirion->SetStandState(UNIT_STAND_STATE_KNEEL);
tirion->DespawnOrUnsummon(20000);
}
scheduler.Schedule(Seconds(12), [this](TaskContext /*context*/)
scheduler.Schedule(Seconds(2), [this](TaskContext context)
{
if (stage != STAGE_HIGHLORD)
return;
// SIGNALE EN JEU : « p7 Tirion reste muet, le script ne se
// lance plus ». La tache sortait SANS SE REPLANIFIER quand
// Tirion n'etait pas encore charge -- sa zone se trouve a
// l'autre bout de la carte, la grille n'est peuplee qu'a
// l'approche du joueur. Elle mourait donc au premier
// passage, et la scene n'avait plus aucune chance de partir.
Creature* tirion = instance->GetCreature(tirionGUID);
if (!tirion)
{
context.Repeat(Seconds(2));
return;
}
bool atteint = false;
DoOnPlayers([&atteint, tirion](Player* player)
{
// SIGNALE EN JEU : « le scenario ne se declenche que si
// on saute dans la lave, le perimetre de detection est
// trop serre ». Tirion agonise au bord du bassin, a
// z=40, Krosus etant a z=35 : a 25 metres, le seul point
// qui satisfaisait la condition etait la lave elle-meme.
if (player->IsWithinDist(tirion, 50.0f, false))
atteint = true;
});
if (!atteint)
{
context.Repeat(Seconds(2));
return;
}
DoSendEventScenario(EVENT_TIRION_REACHED);
stage = STAGE_KROSUS;
CompleteStep();
if (Creature* krosus = Summon(NPC_KROSUS, Anchors().crevasse))
SceneMortTirion();
});
}
// =================================================================
// SylvaniaCore : la mort de Tirion, mise en scene.
//
// SIGNALE EN JEU : « p7 c'est Krosus qui plonge Tirion dans le fiel
// normalement, et la pas de script de scenario ». Le script se
// contentait d'invoquer Krosus douze secondes plus tard.
//
// Sequence officielle, relevee sur Warcraft Wiki :
// Tirion « Stay back... it's a trap... »
// Gul'dan « Ha, you fool! You stand before the temple of a GOD... »
// Krosus surgit de la lave.
// Gul'dan « Destroy him. »
// Krosus souffle sur Tirion, qui sombre sous la lave.
// Thrall « Fordring! »
// Gul'dan « All you have worked for... » puis « Destroy them! »
//
// Les huit repliques portent leur BroadcastTextId et leur Sound
// d'origine : le client joue la voix et affiche sa propre langue.
//
// Gul'dan est invoque des maintenant, au sommet du tombeau. Il y
// domine toute la fin du scenario -- c'est de la qu'il parle, et la
// finale le reutilise au lieu d'en invoquer un second.
// =================================================================
void SceneMortTirion()
{
FactionAnchors const& a = Anchors();
// Gul'dan surplombe deja la scene : rien a invoquer.
if (Creature* guldan = instance->GetCreature(guldanGUID))
{
guldan->SetReactState(REACT_PASSIVE);
guldan->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_NPC | UNIT_FLAG_IMMUNE_TO_PC);
}
auto dire = [this](ObjectGuid const& guid, uint8 groupe)
{
if (Creature* qui = instance->GetCreature(guid))
qui->AI()->Talk(groupe);
};
// 0 s -- Tirion comprend le piege
dire(tirionGUID, 20);
scheduler.Schedule(Seconds(5), [this, dire](TaskContext /*c*/)
{
dire(guldanGUID, 20); // « Ha, you fool!... »
});
scheduler.Schedule(Seconds(11), [this](TaskContext /*c*/)
{
// Krosus est deja dans la lave : on le rend seulement inerte
// le temps de la scene.
if (Creature* krosus = instance->GetCreature(krosusGUID))
{
krosus->SetReactState(REACT_PASSIVE);
krosus->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_NPC | UNIT_FLAG_IMMUNE_TO_PC);
}
});
scheduler.Schedule(Seconds(14), [this, dire](TaskContext /*c*/)
{
dire(guldanGUID, 21); // « Destroy him. »
});
scheduler.Schedule(Seconds(17), [this, dire](TaskContext /*c*/)
{
// Le souffle de Krosus emporte Tirion
Creature* krosus = instance->GetCreature(krosusGUID);
Creature* tirion = instance->GetCreature(tirionGUID);
if (krosus && tirion)
krosus->SetFacingToObject(tirion);
dire(tirionGUID, 21); // « The Light... ahh! »
});
scheduler.Schedule(Seconds(20), [this](TaskContext /*c*/)
{
// Il sombre sous la lave
if (Creature* tirion = instance->GetCreature(tirionGUID))
{
tirion->SetStandState(UNIT_STAND_STATE_DEAD);
tirion->DespawnOrUnsummon(4000);
}
});
scheduler.Schedule(Seconds(23), [this, dire](TaskContext /*c*/)
{
dire(leaderGUID, 20); // riposte du chef
DoOnPlayers([](Player* /*player*/) { });
});
scheduler.Schedule(Seconds(27), [this, dire](TaskContext /*c*/)
{
dire(guldanGUID, 22); // « All you have worked for... »
});
scheduler.Schedule(Seconds(33), [this, dire](TaskContext /*c*/)
{
dire(guldanGUID, 23); // « Destroy them! »
if (Creature* krosus = instance->GetCreature(krosusGUID))
{
krosus->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_NPC | UNIT_FLAG_IMMUNE_TO_PC);
krosus->SetReactState(REACT_AGGRESSIVE);
krosus->SetInCombatWithZone();
}
});
}
void StartFinale()
{
FactionAnchors const& a = Anchors();
if (Creature* guldan = Summon(NPC_GULDAN, a.tomb))
// Gul'dan est deja au sommet du tombeau depuis la mort de
// Tirion : on ne l'invoque une seconde fois que s'il a disparu.
Creature* guldan = instance->GetCreature(guldanGUID);
if (!guldan)
{
guldanGUID = guldan->GetGUID();
guldan->SetReactState(REACT_PASSIVE);
guldan->AI()->Talk(0);
guldan = Summon(NPC_GULDAN, a.tomb);
if (guldan)
{
guldanGUID = guldan->GetGUID();
guldan->SetReactState(REACT_PASSIVE);
}
}
SummonWave(a.tomb, 4);
if (guldan)
guldan->AI()->Talk(0);
// =============================================================
// SIGNALE EN JEU : « la p9 vaincre Gul'dan se valide toute
// seule ». Elle s'achevait apres huit morts de demons -- or le
// script en invoquait lui-meme quatre au tombeau, puis quatre
// autres vingt secondes plus tard. Il declenchait donc sa
// propre condition de fin sans que le joueur y soit pour rien.
//
// L'etape se conclut desormais au terme de la sequence de
// Gul'dan, une fois ses trois repliques prononcees. Le compte
// de morts ne la declenche plus.
// =============================================================
scheduler.Schedule(Seconds(20), [this](TaskContext /*context*/)
{
if (stage == STAGE_STOP_GULDAN && finaleKills < KILLS_FINALE)
SummonWave(Anchors().tomb, 4);
if (Creature* guldan = instance->GetCreature(guldanGUID))
guldan->AI()->Talk(1);
});
scheduler.Schedule(Seconds(38), [this](TaskContext /*context*/)
{
if (stage == STAGE_STOP_GULDAN)
FinishScenario();
});
}
void FinishScenario()
{
stage = STAGE_DONE;
CompleteStep();
DoSendEventScenario(EVENT_GULDAN_STOPPED);
if (Scenario* scenario = instance->GetInstanceScenario())
scenario->CompleteScenario();
@@ -578,6 +1081,13 @@ private:
ObjectGuid leaderGUID;
ObjectGuid jainaGUID;
ObjectGuid sylvanasGUID;
ObjectGuid placedVarianGUID;
ObjectGuid placedVoljinGUID;
ObjectGuid tirionGUID;
ObjectGuid krosusGUID;
ObjectGuid arganothGUID;
ObjectGuid gennGUID;
ObjectGuid thrallGUID;
ObjectGuid guldanGUID;
TaskScheduler scheduler;
};
@@ -586,6 +1096,22 @@ private:
// appelle sScriptMgr->OnGossipHello avant tout traitement specifique, ce
// qui nous donne le seul point d'accroche disponible. On renvoie false
// pour laisser le comportement normal se poursuivre.
// Les cages de la Legion, disseminees dans la cite. Les liberer fait
// avancer la barre de l'etape 6.
class go_legion_cage : public GameObjectScript
{
public:
go_legion_cage() : GameObjectScript("go_legion_cage") { }
bool OnGossipHello(Player* /*player*/, GameObject* go) override
{
if (InstanceScript* instance = go->GetInstanceScript())
instance->SetData(DATA_CAGE_OPENED, 1);
return false; // on laisse le comportement normal se poursuivre
}
};
class go_spire_of_woe : public GameObjectScript
{
public:
@@ -604,4 +1130,5 @@ void AddSC_scenario_broken_shore_intro()
{
RegisterInstanceScript(scenario_broken_shore_intro, 1460);
new go_spire_of_woe();
new go_legion_cage();
}
@@ -126,8 +126,11 @@ public:
if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 0, true))
me->CastSpell(target, SPELL_EXPEL_LIGHT_MARKER, false);
events.ScheduleEvent(EVENT_EXPEL_LIGHT, (expelLightSwitch <= 1) ? 20 : 75 * IN_MILLISECONDS);
expelLightSwitch = (expelLightSwitch <= 1) ? ++expelLightSwitch : 0;
// SylvaniaCore : le 20 n etait pas multiplie par IN_MILLISECONDS
// (relance toutes les 20 ms), et l ancienne affectation modifiait
// expelLightSwitch deux fois sans point de sequence.
events.ScheduleEvent(EVENT_EXPEL_LIGHT, (expelLightSwitch <= 1) ? 20 * IN_MILLISECONDS : 75 * IN_MILLISECONDS);
expelLightSwitch = (expelLightSwitch <= 1) ? expelLightSwitch + 1 : 0;
break;
case EVENT_SHIELD_OF_LIGHT:
@@ -102,7 +102,9 @@ public:
{
case 1:
{
for (uint32 i = 1; i < 7; i++) // +1
// SylvaniaCore : Waypointspawn n a que 6 entrees (0 a 5) ; la boucle
// d origine lisait l index 6, hors du tableau, a chaque declenchement.
for (uint32 i = 0; i < 6; i++)
{
me->CastSpell(Waypointspawn[i].GetPositionX() + urand(0, 25), Waypointspawn[i].GetPositionY() + urand(0, 25), Waypointspawn[i].GetPositionZ(), 217212, true);
}
@@ -592,10 +592,65 @@ public:
QUEST_DOWN_TO_AZSUNA = 41220,
SPELL_TAXI_DALARAN_AZSUNA_ALLIANCE = 205098,
SPELL_TAXI_DALARAN_AZSUNA_HORDE = 205203,
// =============================================================
// SylvaniaCore : l'entree du scenario d'assaut du Rivage brise.
//
// La carte 1666 etait peuplee et scriptee, mais AUCUN chemin
// n'y menait : le joueur restait plante devant Khadgar.
//
// Le deroulement officiel, tel que le decrivent les joueurs :
// on prend la quete aupres de l'archimage a l'Anneau de Krasus,
// puis on choisit « I am ready to launch the assault », une
// cinematique se joue et l'on est EMPORTE PAR LA VOIE DES AIRS
// jusqu'au scenario. C'est notre corbeau arcanique.
//
// Cette option de dialogue n'existe ni dans notre base ni dans
// le dump de reference : c'est de la donnee de capture jamais
// importee. Elle est donc recreee ici, faute de source.
//
// Le sort 240603 porte la destination officielle dans
// spell_target_position : carte 1666, (-1111.6, 2969.71, 186).
// C'est celui-la qu'on lance, pas une teleportation inventee.
//
// 86563 est bien le Khadgar de l'Anneau de Krasus : il est pose
// en (-841, 4258, 746), l'altitude de Dalaran, et il donne deja
// « Assault on Broken Shore ».
// =============================================================
QUEST_ASSAUT_RIVAGE = 46734,
QUEST_LANCER_ASSAUT = 45102,
SPELL_ENTREE_ASSAUT = 240603,
ACTION_LANCER_ASSAUT = GOSSIP_ACTION_INFO_DEF + 90,
};
bool OnGossipSelect(Player* player, Creature* /*creature*/, uint32 /*sender*/, uint32 /*action*/) override
bool OnGossipHello(Player* player, Creature* creature) override
{
// On reconstruit d'abord le menu normal -- quetes comprises.
// Sans cela Khadgar perdrait les seize quetes qu'il propose :
// le coeur saute PrepareGossipMenu des qu'un script repond au
// dialogue.
player->PrepareGossipMenu(creature, creature->GetCreatureTemplate()->GossipMenuId, true);
bool const assautEnCours =
player->GetQuestStatus(QUEST_ASSAUT_RIVAGE) == QUEST_STATUS_INCOMPLETE ||
player->GetQuestStatus(QUEST_LANCER_ASSAUT) == QUEST_STATUS_INCOMPLETE;
if (assautEnCours)
AddGossipItemFor(player, GOSSIP_ICON_CHAT, "Je suis pret a lancer l'assaut.", GOSSIP_SENDER_MAIN, ACTION_LANCER_ASSAUT);
player->SendPreparedGossip(creature);
return true;
}
bool OnGossipSelect(Player* player, Creature* /*creature*/, uint32 /*sender*/, uint32 action) override
{
if (action == ACTION_LANCER_ASSAUT)
{
CloseGossipMenuFor(player);
player->CastSpell(player, SPELL_ENTREE_ASSAUT, true);
return true;
}
if (player->HasQuest(QUEST_DOWN_TO_AZSUNA) || player->GetQuestStatus(QUEST_DOWN_TO_AZSUNA) == QUEST_STATUS_INCOMPLETE)
player->CastSpell(player, player->IsInAlliance() ? SPELL_TAXI_DALARAN_AZSUNA_ALLIANCE : SPELL_TAXI_DALARAN_AZSUNA_HORDE, true); // KillCredit & SendTaxi
@@ -220,6 +220,7 @@ void AddSC_scenario_the_violet_hold_dh();
void AddSC_scenario_mage_art();
void AddSC_scenario_monk_serenitys_end();
void AddSC_scenario_broken_shore_intro();
void AddSC_scenario_assaut_rivage_brise();
void AddSC_scenario_artifact_runner();
// The name of this function should match:
@@ -422,5 +423,6 @@ void AddBrokenIslesScripts()
AddSC_scenario_mage_art();
AddSC_scenario_monk_serenitys_end();
AddSC_scenario_broken_shore_intro();
AddSC_scenario_assaut_rivage_brise();
AddSC_scenario_artifact_runner();
}
+58
View File
@@ -316,6 +316,64 @@ public:
return false;
PhasingHandler::InheritPhaseShift(creature, chr);
// =============================================================
// SylvaniaCore : conserver la phase du maitre de jeu.
//
// SIGNALE EN JEU : « j'ai tente de me creer un point de pierre
// de foyer en invoquant un aubergiste .npc add mais celui-ci
// n'est visible qu'en mode gm, j'ai teste avec plein d'autres
// c'est pareil ».
//
// CE QUI SE PASSAIT
// InheritPhaseShift donne bien la phase du joueur a la creature,
// mais SaveToDB ne l'ecrit pas : Creature.cpp ne retient que
// GetDBPhase(), la phase DECLAREE EN BASE, nulle pour une
// creature creee a la volee. La commande detruit ensuite l'objet
// et le recree depuis la ligne enregistree -- donc sans phase.
// La creature finale est orpheline de phase des la seconde meme.
//
// Le joueur, lui, herite des phases de son aire : dans le port
// de Hurlevent, phase_area declare 6666 et 13306. PhaseShift::
// CanSee exige une intersection, un objet sans phase est donc
// invisible a qui en porte une. Le mode MJ masquait le defaut :
// SetAlwaysVisible court-circuite tout test de phase.
//
// POURQUOI CE CORRECTIF EST LEGITIME
// TrinityCore 3.3.5 fait exactement cela, explicitement :
// creature->Create(..., chr->GetPhaseMaskForSpawn(), ...);
// creature->SaveToDB(map->GetId(), ..., chr->GetPhaseMaskForSpawn());
// et GetPhaseMaskForSpawn ignore volontairement l'etat « MJ voit
// tout » pour retenir la phase ou se trouverait un joueur normal.
// La reecriture du systeme de phases a perdu ce principe en
// chemin : master herite du meme defaut que nous. On le restaure.
//
// Un decalage subsiste avec 3.3.5 : une phase moderne est un
// ENSEMBLE, alors qu'une ligne de `creature` n'a qu'un champ.
// On retient la premiere et on ANNONCE le choix ainsi que les
// autres phases presentes, plutot que de trancher en silence.
// =============================================================
if (creature->GetDBPhase() == 0)
{
auto const& phasesJoueur = chr->GetPhaseShift().GetPhases();
if (!phasesJoueur.empty())
{
uint32 phaseRetenue = phasesJoueur.begin()->Id;
creature->SetDBPhase(int32(phaseRetenue));
std::string listePhases;
for (auto const& phase : phasesJoueur)
{
if (!listePhases.empty())
listePhases += ", ";
listePhases += std::to_string(uint32(phase.Id));
}
handler->PSendSysMessage("Phase %u enregistree sur ce spawn (vous etes dans les phases : %s). Sans elle la creature serait invisible a tout joueur de cette aire.",
phaseRetenue, listePhases.c_str());
}
}
creature->SaveToDB(map->GetId(), { map->GetDifficultyID() });
ObjectGuid::LowType db_guid = creature->GetSpawnId();
+19 -1
View File
@@ -102,14 +102,32 @@ namespace {
if (!group)
return 1;
// Les playerbots ne comptent pas.
//
// SIGNALE EN JEU : « quatre mercenaires contre un gangreseigneur elite,
// ils meurent tous, alors que seul j y arrive ».
//
// Solocraft existe pour compenser l absence de joueurs. Un mercenaire
// n en remplace pas un : sa couche de combat est restee en identifiants
// de sorts WotLK et il ne lance qu une poignee de ses capacites. Le
// compter comme un joueur entier divisait le bonus de l employeur par
// cinq sans rien apporter en face -- l escorte etait strictement
// perdante, un piege plutot qu un arbitrage.
//
// Cela supprime aussi une oscillation : chaque mercenaire qui debarquait
// faisait recalculer et fondre le buff d un cran, d ou les cycles de
// retrait/reapplication visibles dans le journal.
uint32 count = 0;
for (GroupReference* itr = group->GetFirstMember(); itr != nullptr; itr = itr->next())
{
Player* member = itr->GetSource();
if (member && member->IsInWorld() && member->GetMap() == map)
if (member && !member->IsPlayerBot() && member->IsInWorld() && member->GetMap() == map)
++count;
}
// Un groupe entierement compose de bots retombe sur 1 : ils recoivent
// alors le meme bonus qu un joueur seul, ce qui est coherent avec le
// principe -- personne de reel n est la pour porter le contenu.
return std::max<uint32>(count, 1);
}
@@ -1228,9 +1228,12 @@ struct auchindoun_mob_blazing_trickster : public ScriptedAI
if (l_Azzakel->IsInCombat() && l_Azzakel->IsAlive() && l_Azzakel->IsAIEnabled)
l_Azzakel->GetAI()->DoAction(eAuchindounActions::ActionDemonSoulsAchievement);
}
if (me->GetEntry() == 79510)
if (Creature* Azzakel_control = me->FindNearestCreature(87218, 50.0f, true))
Azzakel_control->AI()->DoAction(2);
/// 87218 n'est qu'un marqueur de position sans IA : le compteur doit
/// aller au vrai controleur (76216), retrouve par les donnees d'instance.
if (me->GetEntry() == 79510 && m_Instance != nullptr)
if (Creature* l_Trigger = m_Instance->instance->GetCreature(m_Instance->GetGuidData(eAuchindounDatas::DataTriggerAzzakelController)))
if (l_Trigger->IsWithinDistInMap(me, 50.0f) && l_Trigger->IsAIEnabled)
l_Trigger->AI()->DoAction(eAuchindounActions::ActionCountPre3StBossKill);
}
void UpdateAI(uint32 p_Diff) override
@@ -485,15 +485,17 @@ struct auchindoun_azzakel_mob_controller : public ScriptedAI
}
case eAzzakelEvents::EventSummonAzzakel02:
{
if (!m_Azzakel)
/// la condition etait inversee : on entrait ici uniquement quand
/// le GUID etait vide, donc Azzakel n'etait jamais rendu hostile.
if (!m_Azzakel.IsEmpty())
{
if (Creature* l_Azzakael = ObjectAccessor::GetCreature(*me, m_Azzakel))
{
l_Azzakael->setFaction(HostileFaction);
l_Azzakael->GetMotionMaster()->MoveCharge(1911.93f, 2754.40f, 30.973f, 42.0f);
}
break;
}
break;
}
default:
break;
@@ -108,10 +108,22 @@ public:
{
player->GetSceneMgr().CancelSceneByPackageId(TanaanSceneObjects::SceneFinaleIronBastion);
/// Les scenes du bateau (986 / 953) font planter le client : elles sont ecrites pour
/// se jouer sur un transport en mouvement, alors que PlaySceneByPackageId les envoie
/// ancrees sur la position du joueur, avec un TransportGUID vide et un SceneID nul.
/// On fait donc directement ce que leur declencheur "Teleport" faisait, cf.
/// playerScript_tanaan_scene_boat plus bas (ses hooks restent en place au cas ou la
/// scene serait rejouee a la main).
if (player->GetTeamId() == TEAM_ALLIANCE)
player->GetSceneMgr().PlaySceneByPackageId(TanaanSceneObjects::SceneAllianceBoat);
{
player->TeleportTo(TanaanZones::MapDraenor, 2308.9621f, 454.9409f, 6.0f, player->GetOrientation());
player->CompletedAchievement(TanaanAchievements::AchievementWelcomeToDreanorA);
}
else
player->GetSceneMgr().PlaySceneByPackageId(TanaanSceneObjects::SceneHordeBoat);
{
player->TeleportTo(TanaanZones::MapDraenor, 5538.213379f, 5015.2690f, 13.0f, player->GetOrientation());
player->CompletedAchievement(TanaanAchievements::AchievementWelcomeToDreanorH);
}
}
}
@@ -317,6 +317,12 @@ public:
return true;
player->KilledMonsterCredit(TanaanKillCredits::CreditNorthernSpireDisabled);
/// Ces scenes ne sont PAS la cause du joueur qui passe sous la map, contrairement
/// a ce qui avait ete suppose le 07/09/2026. Verifie en jeu : jouer 961 seule ne
/// produit que la replique de Cho'gall, texte et audio, sans deplacement ni
/// cinematique. Elles avaient ete retirees a tort et sont ici retablies.
/// A ne pas confondre avec les scenes du bateau 953 / 986, elles bel et bien
/// relocalisantes, retirees dans tanaan_intro_finale.cpp pour cette raison.
player->GetSceneMgr().PlaySceneByPackageId(TanaanSceneObjects::SceneChoGallsFreedom);
}
/// Bleeding Hollow Gob (Teron'Gor)
@@ -252,7 +252,7 @@ public:
if (Garrison* garr = player->GetGarrison(GARRISON_TYPE_GARRISON))
{
garr->ToWodGarrison()->TeleportOwnerAndPlayMovie();
garr->ToWodGarrison()->TeleportOwnerToGarrison();
player->KilledMonsterCredit(NPC_ESTABLISH_YOUR_GARRISON_KILL_CREDIT);
}
}
@@ -366,6 +366,45 @@ class spell_groog_rampage : public SpellScriptLoader
}
};
/// 78466 - Gazlowe : retour dans un fief deja fonde.
/// Les deux points d'entree existants (l'objet Master Surveyor cote Horde,
/// Baros Alexston cote Alliance) ne se declenchent que si le joueur n'a PAS
/// encore de fief. Une fois celui-ci fonde, plus rien ne permettait d'y entrer.
class npc_gazlowe_garrison : public CreatureScript
{
public:
npc_gazlowe_garrison() : CreatureScript("npc_gazlowe_garrison") { }
enum
{
GOSSIP_ACTION_ENTER_GARRISON = 1000
};
bool OnGossipHello(Player* player, Creature* creature) override
{
if (creature->IsQuestGiver())
player->PrepareQuestMenu(creature->GetGUID());
/// Gazlowe est aussi spawne a l'interieur des fiefs : on n'y propose rien.
if (player->GetGarrison(GARRISON_TYPE_GARRISON) && !player->GetMap()->IsGarrison())
AddGossipItemFor(player, 0, "Emmenez-moi à mon fief.", 0, GOSSIP_ACTION_ENTER_GARRISON);
SendGossipMenuFor(player, player->GetGossipTextId(creature), creature->GetGUID());
return true;
}
bool OnGossipSelect(Player* player, Creature* /*creature*/, uint32 /*sender*/, uint32 action) override
{
CloseGossipMenuFor(player);
if (action == GOSSIP_ACTION_ENTER_GARRISON)
if (Garrison* garrison = player->GetGarrison(GARRISON_TYPE_GARRISON))
garrison->ToWodGarrison()->TeleportOwnerToGarrison();
return true;
}
};
void AddSC_frostfire_ridge()
{
/* BEGIN */
@@ -376,6 +415,7 @@ void AddSC_frostfire_ridge()
new npc_durotan_frostridge_begin();
new go_frostridge_master_surveyor();
new npc_gazlowe_garrison();
new spell_frostridge_claiming();
@@ -180,20 +180,29 @@ public:
if (player->GetQuestStatus(QUEST_ESTABLISH_YOUR_GARRISON) == QUEST_STATUS_INCOMPLETE)
AddGossipItemFor(player, 60002, 1, 0, 0);
/// Retour dans un fief deja fonde : sans cela plus rien n'y ramene.
if (player->GetGarrison(GARRISON_TYPE_GARRISON) && !player->GetMap()->IsGarrison())
AddGossipItemFor(player, 0, "Emmenez-moi à mon fief.", 0, 1000);
SendGossipMenuFor(player, player->GetGossipTextId(creature), creature->GetGUID());
return true;
}
bool OnGossipSelect(Player* player, Creature* /*creature*/, uint32 /*sender*/, uint32 /*action*/) override
bool OnGossipSelect(Player* player, Creature* /*creature*/, uint32 /*sender*/, uint32 action) override
{
Garrison* garrison = player->GetGarrison(GARRISON_TYPE_GARRISON);
if (!garrison)
{
CloseGossipMenuFor(player);
player->CreateGarrison(player->IsInAlliance() ? GARRISON_SITE_WOD_ALLIANCE : GARRISON_SITE_WOD_HORDE);
player->GetGarrison(GARRISON_TYPE_GARRISON)->ToWodGarrison()->TeleportOwnerAndPlayMovie();
player->GetGarrison(GARRISON_TYPE_GARRISON)->ToWodGarrison()->TeleportOwnerToGarrison();
player->KilledMonsterCredit(NPC_ESTABLISH_YOUR_GARRISON_KILL_CREDIT);
}
else if (action == 1000)
{
CloseGossipMenuFor(player);
garrison->ToWodGarrison()->TeleportOwnerToGarrison();
}
return true;
}
@@ -15,6 +15,7 @@
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "Chat.h"
#include "LFGMgr.h"
#include "LFGQueue.h"
#include "LFGPackets.h"
@@ -340,12 +341,56 @@ public:
player->PlayerTalkClass->ClearMenus();
player->PlayerTalkClass->SendCloseGossip();
// Embarquement pour la Rive Brisee : credit du bateau puis entree dans le scenario 786 (map 1460)
if (player->GetQuestStatus(42740) == QUEST_STATUS_INCOMPLETE)
// =============================================================
// SylvaniaCore : on embarque, on n'atterrit pas sur le sable.
//
// SIGNALE EN JEU : « quand on rejoint le Rivage brise on spawn
// sur un bateau en mouvement qui se dirige vers la plage, on
// n'est pas teleporte directement sur la plage ». Confirme par
// la video : la premiere phase s'intitule « Rendez-vous au
// rivage Brise » et se joue depuis le pont d'un navire.
//
// Ce script deposait le joueur en (443.8, 2076.1, 1.2), le
// point d'ancrage de la plage -- une coordonnee inventee.
//
// Le sort 199358 porte la destination OFFICIELLE dans
// spell_target_position : (441.2, 2023.75, 4.44), c'est-a-dire
// le pont de l'Alliance Battleship, pose en (440, 2025). La
// ligne est marquee VerifiedBuild 27843 : c'est de la donnee
// Blizzard, pas une deduction.
// =============================================================
// SIGNALE EN JEU : « le gossip d'Angelica ne declenche plus
// rien ». Deux causes, corrigees ensemble.
//
// Le lancement du sort 199358 n'aboutissait pas -- rien dans le
// journal, donc il n'etait meme pas tente. On teleporte
// directement aux coordonnees que ce sort porte dans
// spell_target_position, ce qui revient au meme sans dependre
// de la presence du sort cote serveur.
//
// Et la condition exigeait que la quete 42740 soit EN COURS :
// deja rendue ou pas encore prise, l'option restait muette.
// On accepte desormais aussi la quete achevee mais non rendue,
// et on laisse repasser ceux qui l'ont terminee -- refaire le
// scenario ne doit pas etre interdit.
float const PONT_DU_NAVIRE_X = 441.2f;
float const PONT_DU_NAVIRE_Y = 2023.75f;
float const PONT_DU_NAVIRE_Z = 4.44f;
QuestStatus const etat = player->GetQuestStatus(42740);
if (etat == QUEST_STATUS_INCOMPLETE || etat == QUEST_STATUS_COMPLETE ||
etat == QUEST_STATUS_REWARDED)
{
player->KilledMonsterCredit(creature->GetEntry());
player->TeleportTo(1460, 443.8f, 2076.1f, 1.2f, 0.4f);
if (etat == QUEST_STATUS_INCOMPLETE)
player->KilledMonsterCredit(creature->GetEntry());
player->TeleportTo(1460, PONT_DU_NAVIRE_X, PONT_DU_NAVIRE_Y,
PONT_DU_NAVIRE_Z, 0.4f);
}
else
ChatHandler(player->GetSession()).PSendSysMessage(
"Prenez d'abord la quete La bataille du rivage Brise.");
return true;
};
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2008-2018 TrinityCore <https://www.trinitycore.org/>
/*
* This file is part of the DestinyCore Project. See AUTHORS file for Copyright information
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
@@ -15,7 +15,23 @@
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
// Nalorakk n'avait qu'une coquille de script : ni sorts, ni formes, ni les quatre
// vagues de trolls de la rampe.
//
// L'evenement suit AzerothCore, qui tient la meilleure version de ce contenu BC :
// le boss attend au pied de la rampe, lance sur les joueurs le groupe de trolls
// pose autour de lui, ATTEND QUE LA VAGUE SOIT MORTE, puis recule d'un palier en
// lancant « Make way for da Nalorakk! ». Il n'est attaquable qu'apres la quatrieme.
// Les deplacements passent par des positions en dur (reprises de l'ancien script
// TrinityCore) : ni spawn groups ni chemins de waypoints n'existent sur ce core.
//
// Numerotation des textes : celle de NOTRE creature_text (verifiee ligne a ligne),
// qui est l'ancienne numerotation BC et non celle des cores modernes.
#include "ScriptMgr.h"
#include "Creature.h"
#include "MotionMaster.h"
#include "ObjectAccessor.h"
#include "ScriptedCreature.h"
#include "zulaman.h"
@@ -30,17 +46,63 @@ enum Says
SAY_SURGE = 6,
EMOTE_SURGE = 7,
EMOTE_BEAR = 8,
SAY_BEAR = 9,
SAY_TROLL = 10,
SAY_SHIFT_TO_BEAR = 9,
SAY_MAKE_WAY = 10, // « Make way for da Nalorakk! » : il recule d'un palier
SAY_DEATH = 11
};
enum Spells
{
// Forme troll
SPELL_BRUTAL_SWIPE = 42384,
SPELL_MANGLE = 42389,
SPELL_SURGE = 42402,
// Forme d'ours
SPELL_LACERATING_SLASH = 42395,
SPELL_REND_FLESH = 42397,
SPELL_DEAFENING_ROAR = 42398,
// Communs
SPELL_SHAPE_OF_THE_BEAR = 42377,
SPELL_BERSERK = 45078
};
enum Events
{
EVENT_BRUTAL_SWIPE = 1,
EVENT_MANGLE,
EVENT_SURGE,
EVENT_LACERATING_SLASH,
EVENT_REND_FLESH,
EVENT_DEAFENING_ROAR,
EVENT_SHAPESHIFT,
EVENT_BERSERK
};
enum WaveCreatures
{
NPC_AMANISHI_AXE_THROWER = 23542,
NPC_AMANISHI_WARBRINGER = 23580,
NPC_AMANISHI_MEDICINE_MAN = 23581,
NPC_AMANISHI_TRIBESMAN = 23582
};
// Le chemin de la rampe. Les paliers ou il s'arrete pour lancer une vague sont
// 0, 1, 5 et 8 ; les autres points ne sont que des etapes de trajet.
static float const NalorakkWay[9][3] =
{
{ 18.569f, 1414.512f, 11.42f }, // palier 1
{ -17.264f, 1419.551f, 12.62f }, // palier 2
{ -52.642f, 1419.357f, 27.31f },
{ -69.908f, 1419.721f, 27.31f },
{ -79.929f, 1395.958f, 27.31f },
{ -80.072f, 1374.555f, 40.87f }, // palier 3
{ -80.072f, 1314.398f, 40.87f },
{ -80.072f, 1295.775f, 48.60f },
{ -80.072f, 1362.000f, 40.87f } // palier 4
};
class boss_nalorakk : public CreatureScript
@@ -51,17 +113,203 @@ class boss_nalorakk : public CreatureScript
struct boss_nalorakkAI : public BossAI
{
boss_nalorakkAI(Creature* creature) : BossAI(creature, DATA_NALORAKK) { }
boss_nalorakkAI(Creature* creature) : BossAI(creature, DATA_NALORAKK),
_bearForm(false), _waveEvent(true), _waveInProgress(false), _inMove(false),
_wave(0), _currentPoint(0), _targetPoint(0), _checkTimer(0) { }
void Reset() override
{
_Reset();
_bearForm = false;
me->RemoveAurasDueToSpell(SPELL_SHAPE_OF_THE_BEAR);
if (!_waveEvent)
return;
// Une fois l'evenement joue, il ne recommence pas : sans cela un simple
// repli rendrait le boss intouchable pour de bon.
_waveInProgress = false;
_inMove = false;
_wave = 0;
_currentPoint = 0;
_targetPoint = 0;
_checkTimer = 0;
_waveGuids.clear();
me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE | UNIT_FLAG_NON_ATTACKABLE);
me->SetSpeedRate(MOVE_RUN, 2.0f);
me->SetWalk(false);
// Notre spawn est pose a la position finale du boss, en haut de la rampe :
// on le remet au pied, sinon l'evenement se deroulerait a l'envers.
me->NearTeleportTo(NalorakkWay[0][0], NalorakkWay[0][1], NalorakkWay[0][2], 3.14f);
me->SetHomePosition(NalorakkWay[0][0], NalorakkWay[0][1], NalorakkWay[0][2], 3.14f);
}
// Tant que les vagues durent, il ne repond pas aux provocations.
void AttackStart(Unit* who) override
{
if (!_waveEvent)
BossAI::AttackStart(who);
}
void GatherWave(uint32 entry, float radius)
{
std::list<Creature*> found;
me->GetCreatureListWithEntryInGrid(found, entry, radius);
for (Creature* creature : found)
if (creature->IsAlive())
_waveGuids.push_back(creature->GetGUID());
}
void LaunchWave(Unit* target)
{
_waveGuids.clear();
switch (_wave)
{
case 0:
GatherWave(NPC_AMANISHI_AXE_THROWER, 10.0f);
GatherWave(NPC_AMANISHI_TRIBESMAN, 10.0f);
break;
case 1:
GatherWave(NPC_AMANISHI_AXE_THROWER, 10.0f);
GatherWave(NPC_AMANISHI_TRIBESMAN, 10.0f);
GatherWave(NPC_AMANISHI_MEDICINE_MAN, 10.0f);
break;
case 2:
GatherWave(NPC_AMANISHI_WARBRINGER, 10.0f);
break;
case 3:
GatherWave(NPC_AMANISHI_WARBRINGER, 25.0f);
GatherWave(NPC_AMANISHI_MEDICINE_MAN, 25.0f);
break;
default:
break;
}
Talk(uint8(SAY_WAVE_1 + _wave));
for (ObjectGuid const& guid : _waveGuids)
if (Creature* member = ObjectAccessor::GetCreature(*me, guid))
{
member->SetNoCallAssistance(true);
member->AI()->AttackStart(target);
}
// La quatrieme vague est la derniere : il descend dans l'arene.
if (_wave == 3)
{
EndWaveEvent();
return;
}
_waveInProgress = true;
_checkTimer = 2000;
}
bool WaveIsDead() const
{
for (ObjectGuid const& guid : _waveGuids)
if (Creature* member = ObjectAccessor::GetCreature(*me, guid))
if (member->IsAlive())
return false;
return true;
}
// Fin de l'evenement : il redevient un boss ordinaire. Appele aussi en filet
// de securite si un joueur lui arrive au contact, parce qu'une immunite
// d'introduction qui ne se leve jamais rend un boss definitivement intuable.
void EndWaveEvent()
{
if (!_waveEvent)
return;
_waveEvent = false;
_waveInProgress = false;
_inMove = false;
_waveGuids.clear();
me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE | UNIT_FLAG_NON_ATTACKABLE);
me->SetHomePosition(me->GetPositionX(), me->GetPositionY(), me->GetPositionZ(), me->GetOrientation());
}
void MoveInLineOfSight(Unit* who) override
{
if (!_waveEvent)
{
BossAI::MoveInLineOfSight(who);
return;
}
if (who->GetTypeId() != TYPEID_PLAYER || !me->IsHostileTo(who) || !who->IsAlive())
return;
if (me->IsWithinDistInMap(who, 8.0f))
{
EndWaveEvent();
return;
}
if (_waveInProgress || _inMove)
return;
float const range = (_wave == 0 || _wave == 3) ? 50.0f : 40.0f;
if (me->IsWithinDistInMap(who, range))
LaunchWave(who);
}
void MoveToNextStop()
{
Talk(SAY_MAKE_WAY);
_waveInProgress = false;
_inMove = true;
++_wave;
switch (_wave)
{
case 1: _targetPoint = 1; break;
case 2: _targetPoint = 5; break;
case 3: _targetPoint = 8; break;
default: _targetPoint = _currentPoint; break;
}
AdvanceOnePoint();
}
void AdvanceOnePoint()
{
uint32 next = (_currentPoint < 7 && _targetPoint != 8) ? _currentPoint + 1
: (_targetPoint == 8 ? 8 : _targetPoint);
_currentPoint = next;
me->GetMotionMaster()->MovePoint(_currentPoint, NalorakkWay[_currentPoint][0], NalorakkWay[_currentPoint][1], NalorakkWay[_currentPoint][2]);
}
void MovementInform(uint32 type, uint32 id) override
{
if (!_waveEvent || type != POINT_MOTION_TYPE || !_inMove || _currentPoint != id)
return;
if (_currentPoint == _targetPoint)
{
_inMove = false;
me->SetFacingTo(float(M_PI) * 0.5f);
return;
}
AdvanceOnePoint();
}
void EnterCombat(Unit* /*who*/) override
{
Talk(SAY_AGGRO);
_EnterCombat();
ScheduleTrollEvents();
events.ScheduleEvent(EVENT_SHAPESHIFT, 45000);
events.ScheduleEvent(EVENT_BERSERK, 600000);
}
void JustDied(Unit* /*killer*/) override
@@ -76,8 +324,46 @@ class boss_nalorakk : public CreatureScript
Talk(SAY_PLAYER_KILL);
}
void ScheduleTrollEvents()
{
events.ScheduleEvent(EVENT_BRUTAL_SWIPE, urand(7000, 12000));
events.ScheduleEvent(EVENT_MANGLE, urand(10000, 15000));
events.ScheduleEvent(EVENT_SURGE, urand(15000, 20000));
}
void ScheduleBearEvents()
{
events.ScheduleEvent(EVENT_LACERATING_SLASH, 2000);
events.ScheduleEvent(EVENT_REND_FLESH, 3000);
events.ScheduleEvent(EVENT_DEAFENING_ROAR, urand(5000, 10000));
}
void CancelFormEvents()
{
events.CancelEvent(EVENT_BRUTAL_SWIPE);
events.CancelEvent(EVENT_MANGLE);
events.CancelEvent(EVENT_SURGE);
events.CancelEvent(EVENT_LACERATING_SLASH);
events.CancelEvent(EVENT_REND_FLESH);
events.CancelEvent(EVENT_DEAFENING_ROAR);
}
void UpdateAI(uint32 diff) override
{
// Deroulement des vagues : il ne recule qu'une fois la vague abattue.
if (_waveInProgress)
{
if (_checkTimer <= diff)
{
if (WaveIsDead())
MoveToNextStop();
else
_checkTimer = 2000;
}
else
_checkTimer -= diff;
}
if (!UpdateVictim())
return;
@@ -85,19 +371,83 @@ class boss_nalorakk : public CreatureScript
if (me->HasUnitState(UNIT_STATE_CASTING))
return;
/*
while (uint32 eventId = events.ExecuteEvent())
{
switch (eventId)
{
case EVENT_BRUTAL_SWIPE:
DoCastVictim(SPELL_BRUTAL_SWIPE);
events.ScheduleEvent(EVENT_BRUTAL_SWIPE, urand(7000, 12000));
break;
case EVENT_MANGLE:
DoCastVictim(SPELL_MANGLE);
events.ScheduleEvent(EVENT_MANGLE, urand(10000, 15000));
break;
case EVENT_SURGE:
if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, NonTankTargetSelector(me)))
{
Talk(SAY_SURGE);
Talk(EMOTE_SURGE, target);
DoCast(target, SPELL_SURGE);
}
events.ScheduleEvent(EVENT_SURGE, urand(15000, 20000));
break;
case EVENT_LACERATING_SLASH:
DoCastVictim(SPELL_LACERATING_SLASH);
events.ScheduleEvent(EVENT_LACERATING_SLASH, urand(18000, 22000));
break;
case EVENT_REND_FLESH:
DoCastVictim(SPELL_REND_FLESH);
events.ScheduleEvent(EVENT_REND_FLESH, urand(10000, 15000));
break;
case EVENT_DEAFENING_ROAR:
DoCast(me, SPELL_DEAFENING_ROAR);
events.ScheduleEvent(EVENT_DEAFENING_ROAR, urand(10000, 15000));
break;
case EVENT_SHAPESHIFT:
CancelFormEvents();
_bearForm = !_bearForm;
if (_bearForm)
{
Talk(SAY_SHIFT_TO_BEAR);
Talk(EMOTE_BEAR);
DoCast(me, SPELL_SHAPE_OF_THE_BEAR);
ScheduleBearEvents();
events.ScheduleEvent(EVENT_SHAPESHIFT, urand(20000, 25000));
}
else
{
me->RemoveAurasDueToSpell(SPELL_SHAPE_OF_THE_BEAR);
ScheduleTrollEvents();
events.ScheduleEvent(EVENT_SHAPESHIFT, urand(45000, 50000));
}
break;
case EVENT_BERSERK:
DoCast(me, SPELL_BERSERK);
break;
default:
break;
}
if (me->HasUnitState(UNIT_STATE_CASTING))
return;
}
*/
DoMeleeAttackIfReady();
}
private:
bool _bearForm;
bool _waveEvent;
bool _waveInProgress;
bool _inMove;
uint32 _wave;
uint32 _currentPoint;
uint32 _targetPoint;
uint32 _checkTimer;
std::vector<ObjectGuid> _waveGuids;
};
CreatureAI* GetAI(Creature* creature) const override
@@ -20,10 +20,27 @@
#include "GameObject.h"
#include "InstanceScript.h"
#include "Map.h"
#include "Player.h"
#include "ScriptedCreature.h"
#include "WorldStatePackets.h"
#include "zulaman.h"
// Les quatre captifs liberes par la course contre la montre. Chacun apparait la
// ou son boss vient de tomber et remet un coffre de recompense quand on lui parle.
struct HostageInfoEntry
{
uint32 Npc;
float X, Y, Z, O;
};
static HostageInfoEntry const HostageInfo[4] =
{
{ 23790, -57.0f, 1343.0f, 40.77f, 3.2f }, // Tanzar - ours (Nalorakk)
{ 23999, 400.0f, 1414.0f, 74.36f, 3.3f }, // Harkor - aigle (Akil'zon)
{ 24001, -35.0f, 1134.0f, 18.71f, 1.9f }, // Ashli - dragonhalc (Jan'alai)
{ 24024, 413.0f, 1117.0f, 6.32f, 3.1f } // Kraz - lynx (Halazzi)
};
class instance_zulaman : public InstanceMapScript
{
public:
@@ -139,6 +156,28 @@ class instance_zulaman : public InstanceMapScript
return ObjectGuid::Empty;
}
// Rien n'invoquait les captifs : la course contre la montre se deroulait
// jusqu'au bout sans que personne n'apparaisse, donc sans recompense.
void SummonHostage(uint8 num)
{
if (ZulAmanState != IN_PROGRESS)
return;
Map::PlayerList const& playerList = instance->GetPlayers();
if (playerList.isEmpty())
return;
if (Player* player = playerList.begin()->GetSource())
{
HostageInfoEntry const& hostageInfo = HostageInfo[num];
if (Creature* hostage = player->SummonCreature(hostageInfo.Npc, hostageInfo.X, hostageInfo.Y, hostageInfo.Z, hostageInfo.O, TEMPSUMMON_DEAD_DESPAWN))
{
hostage->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE);
hostage->SetFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP);
}
}
}
void SetData(uint32 type, uint32 data) override
{
switch (type)
@@ -202,12 +241,21 @@ class instance_zulaman : public InstanceMapScript
switch (type)
{
case DATA_AKILZON:
if (state == DONE)
SummonHostage(1);
break;
case DATA_NALORAKK:
if (state == DONE)
SummonHostage(0);
break;
case DATA_JANALAI:
if (state == DONE)
SummonHostage(2);
break;
case DATA_HALAZZI:
if (state == DONE)
SummonHostage(3);
break;
case DATA_HEXLORD:
case DATA_DAAKARA:
break;
@@ -250,8 +250,73 @@ class spell_banging_the_gong : public SpellScriptLoader
}
};
// Tanzar, Harkor, Ashli et Kraz : les captifs liberes par la course contre la
// montre. Ils sont invoques par l'instance a la mort de leur boss ; leur seule
// raison d'etre est de remettre le coffre de recompense quand on leur parle.
// Rien ne les scriptait, donc meme invoques ils n'auraient rien donne.
class npc_zulaman_hostage : public CreatureScript
{
public:
npc_zulaman_hostage() : CreatureScript("npc_zulaman_hostage") { }
enum Hostages
{
NPC_TANZAR = 23790,
NPC_HARKOR = 23999,
NPC_ASHLI = 24001,
NPC_KRAZ = 24024,
GO_HAZLEKS_TRUNK = 186648, // Tanzar
GO_BAKKALZUS_SATCHEL = 187021, // Harkor
GO_KASHAS_BAG = 186672, // Ashli
GO_NORKANIS_PACKAGE = 186667, // Kraz
ACTION_FREE_HOSTAGE = GOSSIP_ACTION_INFO_DEF + 1
};
static uint32 ChestForHostage(uint32 entry)
{
switch (entry)
{
case NPC_TANZAR: return GO_HAZLEKS_TRUNK;
case NPC_HARKOR: return GO_BAKKALZUS_SATCHEL;
case NPC_ASHLI: return GO_KASHAS_BAG;
case NPC_KRAZ: return GO_NORKANIS_PACKAGE;
default: return 0;
}
}
bool OnGossipHello(Player* player, Creature* creature) override
{
AddGossipItemFor(player, GOSSIP_ICON_CHAT, "Vous etes libre ! Partez d'ici !", GOSSIP_SENDER_MAIN, ACTION_FREE_HOSTAGE);
SendGossipMenuFor(player, player->GetGossipTextId(creature), creature->GetGUID());
return true;
}
bool OnGossipSelect(Player* player, Creature* creature, uint32 /*sender*/, uint32 action) override
{
CloseGossipMenuFor(player);
if (action != ACTION_FREE_HOSTAGE)
return true;
// Un seul coffre par captif, quel que soit le nombre de joueurs qui lui parlent.
if (!creature->HasFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP))
return true;
creature->RemoveFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP);
if (uint32 chest = ChestForHostage(creature->GetEntry()))
creature->SummonGameObject(chest, creature->GetPositionX() - 2.0f, creature->GetPositionY(),
creature->GetPositionZ(), 0.0f, QuaternionData(), WEEK);
return true;
}
};
void AddSC_zulaman()
{
new npc_zulaman_hostage();
new npc_voljin_zulaman();
new spell_banging_the_gong();
}
@@ -539,6 +539,10 @@ class boss_alysrazor : public CreatureScript
RemoveEncounterAuras();
me->SetCanFly(false);
me->SetDisableGravity(false);
// Elle meurt en vol : laissee a elle-meme, la depouille traverse le vide
// entre les plateformes et finit dans la lave, butin compris. On la ramene
// au centre de l'arene avant la chute, comme en jeu officiel.
me->NearTeleportTo(centerPos.GetPositionX(), centerPos.GetPositionY(), centerPos.GetPositionZ(), centerPos.GetOrientation());
me->GetMotionMaster()->MoveFall();
}
@@ -665,7 +665,9 @@ public:
void ExitVehicle()
{
GetCaster()->ExitVehicle();
// Meme prudence : rien ne garantit la presence du lanceur.
if (Unit* caster = GetCaster())
caster->ExitVehicle();
}
void Register() override
@@ -692,12 +694,41 @@ public:
void HandleScript(SpellEffIndex /*effIndex*/)
{
Creature* target = GetHitUnit()->ToCreature();
// =============================================================
// SylvaniaCore : objet de visee jamais verifie -> SIGSEGV.
//
// SIGNALE EN JEU : « si on attend la phase deux sans le
// frapper, le serveur plante ». Reproduction fidele, et deux
// vidages memoire l'ont confirmee, pile en main :
// #0 spell_seismic_shard::HandleScript
// #1 Spell::HandleEffects
// ...
// #13 InstanceMap::Update
//
// GetDynObject renvoie un pointeur NUL quand l'objet de visee
// n'existe pas -- pas encore cree, ou deja expire. La ligne
// suivante le dereferencait sans le moindre test, ce qui
// emporte le processus entier : le plantage a lieu dans le
// fil de mise a jour de la carte, pas dans une session.
//
// GetCaster() et GetHitUnit() sont verifies au passage : rien
// ne garantit leur presence au moment ou l'effet est traite,
// et un lanceur mort entre le lancement et l'impact suffit.
// =============================================================
Unit* caster = GetCaster();
Unit* hit = GetHitUnit();
if (!caster || !hit)
return;
Creature* target = hit->ToCreature();
if (!target)
return;
DynamicObject* dynamicObject = caster->GetDynObject(SPELL_SEISMIC_SHARD_TARGETING);
if (!dynamicObject)
return;
target->ExitVehicle();
DynamicObject* dynamicObject = GetCaster()->GetDynObject(SPELL_SEISMIC_SHARD_TARGETING);
target->CastSpell(dynamicObject->GetPositionX(), dynamicObject->GetPositionY(), dynamicObject->GetPositionZ(), SPELL_SEISMIC_SHARD_MISSLE, true);
}
@@ -5091,7 +5091,10 @@ struct spell_area_paragons_of_the_klaxxi_reaction_yellow : AreaTriggerAI
caster->AddAura(SPELL_NOXIOUS_VAPORS, target);
}
bool OnRemoveTarget(Unit* target, bool /*byExpire*/)
// SylvaniaCore : le hook du core est OnUnitExit -- OnRemoveTarget n existe
// nulle part dans AreaTriggerAI, la methode n etait donc jamais appelee et
// l aura restait sur le joueur apres sa sortie de la zone.
void OnUnitExit(Unit* target) override
{
target->RemoveAura(SPELL_NOXIOUS_VAPORS);
}
@@ -5139,7 +5142,10 @@ struct spell_area_paragons_of_the_klaxxi_sonic_projection : AreaTriggerAI
caster->AddAura(SPELL_SONIC_PROJECTION_DMG, target);
}
bool OnRemoveTarget(Unit* target, bool /*byExpire*/)
// SylvaniaCore : le hook du core est OnUnitExit -- OnRemoveTarget n existe
// nulle part dans AreaTriggerAI, la methode n etait donc jamais appelee et
// l aura restait sur le joueur apres sa sortie de la zone.
void OnUnitExit(Unit* target) override
{
target->RemoveAura(SPELL_SONIC_PROJECTION_DMG);
}
@@ -3348,8 +3348,12 @@ struct spell_area_siegecrafter_blackfuse_laser_ground_effect : AreaTriggerAI
caster->AddAura(SPELL_SUPERHEATED_AURA, p_Target);
}
bool OnRemoveTarget(Unit* target, bool /*byExpire*/)
// SylvaniaCore : le hook du core est OnUnitExit -- OnRemoveTarget n existe
// nulle part dans AreaTriggerAI, la methode n etait donc jamais appelee et
// l aura restait sur le joueur apres sa sortie de la zone.
void OnUnitExit(Unit* target) override
{
target->RemoveAura(SPELL_SUPERHEATED_AURA);
}
};
@@ -1054,10 +1054,12 @@ struct spell_area_pressure_plate : AreaTriggerAI
unit->AddAura(eSpells::PressurePlateButton, unit);
}
bool OnRemoveTarget(Unit* p_Target, bool /*p_ByExpire*/)
// SylvaniaCore : le hook du core est OnUnitExit -- OnRemoveTarget n existe
// nulle part dans AreaTriggerAI, la methode n etait donc jamais appelee et
// l aura restait sur le joueur apres sa sortie de la zone.
void OnUnitExit(Unit* p_Target) override
{
p_Target->RemoveAura(eSpells::PressurePlateButton);
return true;
}
};

Some files were not shown because too many files have changed in this diff Show More