- // why is this here: is there a mediawiki like diff function for staff?
- $this->addScript([CSS_STRING, 'li input[type="radio"] {margin:0}']);
-
- $this->typeId = $this->_get['id']; // just to display sensible not-found msg
- if ($id = DB::Aowow()->selectCell('SELECT `id` FROM ?_guides WHERE `id` = ?d', $this->typeId))
- $this->typeId = intVal($id);
-
- break;
- case 'new':
- if (User::canWriteGuide())
- {
- $this->show = self::SHOW_NEW;
- $this->guideRevision = null;
-
- $this->initNew();
- return; // do not create new GuideList
- }
- break;
- case 'edit':
- if (User::canWriteGuide())
- {
- if (!$this->initEdit())
- $this->notFound(Lang::guide('guide'), Lang::guide('notFound'));
-
- $this->show = self::SHOW_EDITOR;
- }
- break;
- default:
- if ($id = DB::Aowow()->selectCell('SELECT `id` FROM ?_guides WHERE `url` = ?', Util::lower($guide[0])))
- {
- $this->typeId = intVal($id);
- $this->guideRevision = null;
- $this->articleUrl = Util::lower($guide[0]);
- }
- }
- }
-
-
- /*********************/
- /* load actual guide */
- /*********************/
-
- $this->subject = new GuideList(array(['id', $this->typeId]));
- if ($this->subject->error)
- $this->notFound(Lang::guide('guide'), Lang::guide('notFound'));
-
- if (!$this->subject->canBeViewed() && !$this->subject->userCanView())
- header('Location: ?guides='.$this->subject->getField('category'), true, 302);
-
- if ($this->show == self::SHOW_GUIDE && $this->_get['rev'] !== null && !$this->articleUrl && $this->subject->userCanView())
- $this->guideRevision = $this->_get['rev'];
- else if ($this->show == self::SHOW_GUIDE && !$this->articleUrl)
- $this->guideRevision = $this->subject->getField('rev');
- else
- $this->guideRevision = null;
-
- if (!$this->name)
- $this->name = $this->subject->getField('name');
- }
-
- protected function generateContent() : void
- {
- /*
- match ($this->show)
- {
- self::SHOW_NEW => $this->displayNew(),
- self::SHOW_EDITOR => $this->displayEditor(),
- self::SHOW_GUIDE => $this->displayGuide(),
- self::SHOW_CHANGELOG => $this->displayChangelog(),
- default => trigger_error('GuidePage::generateContent - what content!?')
- };
- */
- switch ($this->show)
- {
- case self::SHOW_NEW:
- $this->displayNew();
- break;
- case self::SHOW_EDITOR:
- $this->displayEditor();
- break;
- case self::SHOW_GUIDE:
- $this->displayGuide();
- break;
- case self::SHOW_CHANGELOG:
- $this->displayChangelog();
- break;
- default:
- trigger_error('GuidePage::generateContent - what content!?');
- }
- }
-
- private function displayNew() : void
- {
- // init required template vars
- $this->editorFields = array(
- 'locale' => User::$localeId,
- 'status' => GUIDE_STATUS_DRAFT
- );
- }
-
- private function displayEditor() : void
- {
- // can't check in init as subject is unknown
- if ($this->subject->getField('status') == GUIDE_STATUS_ARCHIVED)
- $this->notFound(Lang::guide('guide'), Lang::guide('notFound'));
-
- $status = GUIDE_STATUS_NONE;
- $rev = DB::Aowow()->selectCell('SELECT `rev` FROM ?_articles WHERE `type` = ?d AND `typeId` = ?d ORDER BY `rev` DESC LIMIT 1', Type::GUIDE, $this->typeId);
- $curStatus = DB::Aowow()->selectCell('SELECT `status` FROM ?_guides WHERE `id` = ?d ', $this->typeId);
- if ($rev === null)
- $rev = 0;
-
- if ($this->save)
- {
- $rev++;
-
- // insert Article
- DB::Aowow()->query('INSERT INTO ?_articles (`type`, `typeId`, `locale`, `rev`, `editAccess`, `article`) VALUES (?d, ?d, ?d, ?d, ?d, ?)',
- Type::GUIDE, $this->typeId, $this->_post['locale'], $rev, User::$groups, $this->_post['body']);
-
- // link to Guide
- $guideData = array(
- 'category' => $this->_post['category'],
- 'classId' => $this->_post['classId'],
- 'specId' => $this->_post['specId'],
- 'title' => $this->_post['title'],
- 'name' => $this->_post['name'],
- 'description' => $this->_post['description'] ?: Lang::trimTextClean((new Markup($this->_post['body']))->stripTags(), 120),
- 'locale' => $this->_post['locale'],
- 'roles' => User::$groups,
- 'status' => GUIDE_STATUS_DRAFT
- );
-
- DB::Aowow()->query('UPDATE ?_guides SET ?a WHERE `id` = ?d', $guideData, $this->typeId);
-
- // new guide -> reload editor
- if ($this->_get['id'] === 0)
- header('Location: ?guide=edit&id='.$this->typeId, true, 302);
- else
- DB::Aowow()->query('INSERT INTO ?_guides_changelog (`id`, `rev`, `date`, `userId`, `msg`) VALUES (?d, ?d, ?d, ?d, ?)', $this->typeId, $rev, time(), User::$id, $this->_post['changelog']);
-
- if ($this->_post['submit'])
- {
- $status = GUIDE_STATUS_REVIEW;
- if ($curStatus != GUIDE_STATUS_REVIEW)
- {
- DB::Aowow()->query('UPDATE ?_guides SET `status` = ?d WHERE `id` = ?d', GUIDE_STATUS_REVIEW, $this->typeId);
- DB::Aowow()->query('INSERT INTO ?_guides_changelog (`id`, `date`, `userId`, `status`) VALUES (?d, ?d, ?d, ?d)', $this->typeId, time(), User::$id, GUIDE_STATUS_REVIEW);
- }
- }
- }
-
- // init required template vars
- $this->editorFields = array(
- 'category' => $this->_post['category'] ?? $this->subject->getField('category'),
- 'title' => $this->_post['title'] ?? $this->subject->getField('title'),
- 'name' => $this->_post['name'] ?? $this->subject->getField('name'),
- 'description' => $this->_post['description'] ?? $this->subject->getField('description'),
- 'text' => $this->_post['body'] ?? $this->subject->getArticle(),
- 'status' => $status ?: $this->subject->getField('status'),
- 'classId' => $this->_post['classId'] ?? $this->subject->getField('classId'),
- 'specId' => $this->_post['specId'] ?? $this->subject->getField('specId'),
- 'locale' => $this->_post['locale'] ?? $this->subject->getField('locale'),
- 'rev' => $rev
- );
-
- $this->extendGlobalData($this->subject->getJSGlobals());
- }
-
- private function displayGuide() : void
- {
- if (!($this->subject->getField('cuFlags') & GUIDE_CU_NO_QUICKFACTS))
- {
- $qf = [];
- if ($this->subject->getField('cuFlags') & CC_FLAG_STICKY)
- $qf[] = '[span class=guide-sticky]'.Lang::guide('sticky').'[/span]';
-
- $qf[] = Lang::guide('author').Lang::main('colon').'[url=?user='.$this->subject->getField('author').']'.$this->subject->getField('author').'[/url]';
-
- if ($this->subject->getField('category') == 1)
- {
- $c = $this->subject->getField('classId');
- $s = $this->subject->getField('specId');
- if ($c > 0)
- {
- $this->extendGlobalIds(Type::CHR_CLASS, $c);
- $qf[] = Util::ucFirst(Lang::game('class')).Lang::main('colon').'[class='.$c.']';
- }
- if ($s > -1)
- $qf[] = Lang::guide('spec').Lang::main('colon').'[icon class="c'.$c.' icontiny" name='.Game::$specIconStrings[$c][$s].']'.Lang::game('classSpecs', $c, $s).'[/icon]';
- }
-
- // $qf[] = Lang::guide('patch').Lang::main('colon').'3.3.5'; // replace with date
- $qf[] = Lang::guide('added').Lang::main('colon').'[tooltip name=added]'.date('l, G:i:s', $this->subject->getField('date')).'[/tooltip][span class=tip tooltip=added]'.date(Lang::main('dateFmtShort'), $this->subject->getField('date')).'[/span]';
-
- switch ($this->subject->getField('status'))
- {
- case GUIDE_STATUS_APPROVED:
- $qf[] = Lang::guide('views').Lang::main('colon').'[n5='.$this->subject->getField('views').']';
-
- if (!($this->subject->getField('cuFlags') & GUIDE_CU_NO_RATING))
- {
- $this->guideRating = array(
- $this->subject->getField('rating'), // avg rating
- User::canUpvote() && User::canDownvote() ? 'true' : 'false',
- $this->subject->getField('_self'), // my rating amt; 0 = no vote
- $this->typeId // guide Id
- );
-
- if ($this->subject->getField('nvotes') < 5)
- $qf[] = Lang::guide('rating').Lang::main('colon').Lang::guide('noVotes');
- else
- $qf[] = Lang::guide('rating').Lang::main('colon').Lang::guide('votes', [round($this->rating['avg'], 1), $this->rating['n']]);
- }
- break;
- case GUIDE_STATUS_ARCHIVED:
- $qf[] = Lang::guide('status', GUIDE_STATUS_ARCHIVED);
- break;
- }
-
- $qf = '[ul][li]'.implode('[/li][li]', $qf).'[/li][/ul]';
-
- if ($this->subject->getField('status') == GUIDE_STATUS_REVIEW && User::isInGroup(U_GROUP_STAFF) && $this->_get['rev'])
- {
- $this->addScript([JS_STRING, '
- DomContentLoaded.addEvent(function() {
- let send = function (status)
- {
- let message = "";
- let id = $WH.g_getGets().guide;
- if (status == 4) // rejected
- {
- while (message === "")
- message = prompt("Please provide your reasoning.");
-
- if (message === null)
- return false;
- }
-
- $.ajax({cache: false, url: "?admin=guide", type: "POST",
- error: function() {
- alert("Operation failed.");
- },
- success: function(json) {
- if (json != 1)
- alert("Operation failed.");
- else
- window.location.href = "?admin=guides";
- },
- data: { id: id, status: status, msg: message }
- })
-
- return true;
- };
-
- $WH.ge("btn-accept").onclick = send.bind(null, 3);
- $WH.ge("btn-reject").onclick = send.bind(null, 4);
- });
- ']);
-
- $qf .= '[h3 style="text-align:center"]Admin[/h3]';
-
- $qf .= '[div style="text-align:center"][url=# id="btn-accept" class=icon-tick]Approve[/url][url=# style="margin-left:20px" id="btn-reject" class=icon-delete]Reject[/url][/div]';
- }
- }
-
- $this->redButtons[BUTTON_GUIDE_LOG] = true;
- $this->redButtons[BUTTON_GUIDE_REPORT] = $this->subject->canBeReported();
-
- $this->infobox = $qf ?? '';
- $this->author = $this->subject->getField('author'); // add to g_pageInfo in GenericPage:prepareContent()
-
- if ($this->subject->userCanView())
- $this->redButtons[BUTTON_GUIDE_EDIT] = User::canWriteGuide() && $this->subject->getField('status') != GUIDE_STATUS_ARCHIVED;
-
- // the article text itself is added by GenericPage::addArticle()
- }
-
- private function displayChangelog() : void
- {
- $this->addScript([JS_STRING, '
- $(document).ready(function() {
- var radios = $("input[type=radio]");
- function limit(col, val) {
- radios.each(function(i, e) {
- if (col == e.name)
- return;
-
- if (col == "b")
- e.disabled = (val <= parseInt(e.value));
- else if (col == "a")
- e.disabled = (val >= parseInt(e.value));
- });
-
- };
-
- radios.each(function (i, e) {
- e.onchange = limit.bind(this, e.name, parseInt(e.value));
-
- if (i < 2 && e.name == "b") // first pair
- $(e).trigger("click");
- else if (e.value == 0 && e.name == "a") // last pair
- $(e).trigger("click");
- });
- });
- ']);
-
- $buff = '
';
- $inp = fn($rev) => User::isInGroup(U_GROUP_STAFF) ? ($rev !== null ? '' : '') : '';
-
- $logEntries = DB::Aowow()->select('SELECT a.`displayName` AS `name`, gcl.`date`, gcl.`status`, gcl.`msg`, gcl.`rev` FROM ?_guides_changelog gcl JOIN ?_account a ON a.`id` = gcl.`userId` WHERE gcl.`id` = ?d ORDER BY gcl.`date` DESC', $this->typeId);
- foreach ($logEntries as $log)
- {
- if ($log['status'] != GUIDE_STATUS_NONE)
- $buff .= '- '.$inp($log['rev']).Lang::guide('clStatusSet', [Lang::guide('status', $log['status'])]).Lang::main('colon').'
'.Util::formatTimeDiff($log['date'])."\n";
- else if ($log['msg'])
- $buff .= '- '.$inp($log['rev']).Util::formatTimeDiff($log['date']).Lang::main('colon').'
'.$log['msg'].' '.Lang::main('byUser', [$log['name'], 'style="text-decoration:underline"'])."\n";
- else
- $buff .= '- '.$inp($log['rev']).Util::formatTimeDiff($log['date']).Lang::main('colon').''.Lang::guide('clMinorEdit').' '.Lang::main('byUser', [$log['name'], 'style="text-decoration:underline"'])."
\n";
- }
-
- // append creation
- $buff .= '- '.$inp(0).''.Lang::guide('clCreated').Lang::main('colon').''.Util::formatTimeDiff($this->subject->getField('date'))."
\n
\n";
-
-
- if (User::isInGroup(U_GROUP_STAFF))
- $buff .= '
';
-
- $this->name = lang::guide('clTitle', [$this->typeId, $this->subject->getField('title')]);
- $this->extraHTML = $buff;
- }
-
- private function initNew() : void
- {
- $this->addScript(
- [JS_FILE, 'article-description.js'],
- [JS_FILE, 'article-editing.js'],
- [JS_FILE, 'guide-editing.js'],
- [JS_FILE, 'fileuploader.js'],
- [JS_FILE, 'toolbar.js'],
- [JS_FILE, 'AdjacentPreview.js'],
- [CSS_FILE, 'article-editing.css'],
- [CSS_FILE, 'fileuploader.css'],
- [CSS_FILE, 'guide-edit.css'],
- [CSS_FILE, 'AdjacentPreview.css'],
-
- [CSS_STRING, '#upload-result input[type=text] { padding: 0px 2px; font-size: 12px; }'],
- [CSS_STRING, '#upload-result > span { display:block; height: 22px; }'],
- [CSS_STRING, '#upload-result { display: inline-block; text-align:right; }'],
- [CSS_STRING, '#upload-progress { display: inline-block; margin-right:8px; }']
- );
-
- $this->articleUrl = 'new';
- $this->tpl = 'guide-edit';
- $this->name = Lang::guide('newTitle');
-
- Lang::sort('guide', 'category');
-
- $this->typeId = 0; // signals 'edit' to create new guide
- }
-
- private function initEdit() : bool
- {
- $this->addScript(
- [JS_FILE, 'article-description.js'],
- [JS_FILE, 'article-editing.js'],
- [JS_FILE, 'guide-editing.js'],
- [JS_FILE, 'fileuploader.js'],
- [JS_FILE, 'toolbar.js'],
- [JS_FILE, 'AdjacentPreview.js'],
- [CSS_FILE, 'article-editing.css'],
- [CSS_FILE, 'fileuploader.css'],
- [CSS_FILE, 'guide-edit.css'],
- [CSS_FILE, 'AdjacentPreview.css'],
-
- [CSS_STRING, '#upload-result input[type=text] { padding: 0px 2px; font-size: 12px; }'],
- [CSS_STRING, '#upload-result > span { display:block; height: 22px; }'],
- [CSS_STRING, '#upload-result { display: inline-block; text-align:right; }'],
- [CSS_STRING, '#upload-progress { display: inline-block; margin-right:8px; }']
- );
-
- $this->articleUrl = 'edit';
- $this->tpl = 'guide-edit';
- $this->name = Lang::guide('editTitle');
- $this->save = $this->_post['save'] || $this->_post['submit'];
-
- // reject inconsistent guide data
- if ($this->save)
- {
- // req: set data
- if (!$this->_post['title'] || !$this->_post['name'] || !$this->_post['body'] || $this->_post['locale'] === null)
- return false;
-
- // req: valid data
- if (!in_array($this->_post['category'], $this->validCats) || !(CFG_LOCALES & (1 << $this->_post['locale'])))
- return false;
-
- // sanitize: spec / class
- if ($this->_post['category'] == 1) // Classes
- {
- if ($this->_post['classId'] && !((1 << $this->_post['classId']) & CLASS_MASK_ALL))
- $this->_post['classId'] = 0;
-
- if (!in_array($this->_post['specId'], [-1, 0, 1, 2]))
- $this->_post['specId'] = -1;
- if ($this->_post['specId'] > -1 && !$this->_post['classId'])
- $this->_post['specId'] = -1;
- }
- else
- {
- $this->_post['classId'] = 0;
- $this->_post['specId'] = -1;
- }
- }
-
- if ($this->_get['id']) // edit existing guide
- {
- $this->typeId = $this->_get['id']; // just to display sensible not-found msg
- if ($id = DB::Aowow()->selectCell('SELECT `id` FROM ?_guides WHERE `id` = ?d AND `status` <> ?d {AND `userId` = ?d}', $this->typeId, GUIDE_STATUS_ARCHIVED, User::isInGroup(U_GROUP_STAFF) ? DBSIMPLE_SKIP : User::$id))
- $this->typeId = intVal($id);
- }
- else if ($this->_get['id'] === 0) // create new guide and load in editor
- $this->typeId = DB::Aowow()->query('INSERT INTO ?_guides (`userId`, `date`, `status`) VALUES (?d, ?d, ?d)', User::$id, time(), GUIDE_STATUS_DRAFT);
-
- return $this->typeId > 0;
- }
-
- protected function editorFields(string $field, bool $asInt = false) : string|int
- {
- return $this->editorFields[$field] ?? ($asInt ? 0 : '');
- }
-
- protected function generateTooltip()
- {
- $power = new StdClass();
- if (!$this->subject->error)
- {
- $power->{'name_'.User::$localeString} = $this->name;
- $power->{'tooltip_'.User::$localeString} = $this->subject->renderTooltip();
- }
-
- return sprintf($this->powerTpl, Util::toJSON($this->articleUrl ?: $this->typeId), User::$localeId, Util::toJSON($power, JSON_AOWOW_POWER));
- }
-
- protected function generatePath() : void
- {
- if ($x = $this->subject?->getField('category'))
- $this->path[] = $x;
- }
-
- protected function generateTitle() : void
- {
- if ($this->show == self::SHOW_EDITOR)
- array_unshift($this->title, Lang::guide('editTitle').Lang::main('colon').$this->subject->getField('title'), Lang::guide('guides'));
- if ($this->show == self::SHOW_NEW)
- array_unshift($this->title, Lang::guide('newTitle'), Lang::guide('guides'));
- else
- array_unshift($this->title, $this->subject->getField('title'), Lang::guide('guides'));
- }
-
- protected function postCache() : void
- {
- // increment views of published guide; ignore caching
- if ($this->subject?->getField('status') == GUIDE_STATUS_APPROVED)
- DB::Aowow()->query('UPDATE ?_guides SET `views` = `views` + 1 WHERE `id` = ?d', $this->typeId);
- }
-}
-
-?>
diff --git a/pages/guides.php b/pages/guides.php
deleted file mode 100644
index 0e039c14..00000000
--- a/pages/guides.php
+++ /dev/null
@@ -1,100 +0,0 @@
-getCategoryFromUrl($pageParam);
-
- parent::__construct($pageCall, $pageParam);
-
- if ($pageCall == 'my-guides')
- {
- if (!User::$id)
- $this->error();
-
- $this->name = Util::ucFirst(Lang::guide('myGuides'));
- $this->myGuides = true;
- }
- else
- $this->name = Util::ucFirst(Lang::guide('guides'));
- }
-
- protected function generateContent()
- {
- $hCols = ['patch']; // pointless: display date instead
- $vCols = [];
- $xCols = ['$Listview.extraCols.date']; // ok
-
- if ($this->myGuides)
- {
- $conditions = [['userId', User::$id]];
- $hCols[] = 'author';
- $vCols[] = 'status';
- }
- else
- {
- $conditions = array(
- ['locale', User::$localeId],
- ['status', GUIDE_STATUS_ARCHIVED, '!'], // never archived guides
- [
- 'OR',
- ['status', GUIDE_STATUS_APPROVED], // currently approved
- ['rev', 0, '>'] // has previously approved revision
- ]
- );
- if (isset($this->category[0]))
- $conditions[] = ['category', $this->category];
- }
-
- $data = [];
- $guides = new GuideList($conditions);
- if (!$guides->error)
- $data = array_values($guides->getListviewData());
-
- $tabData = array(
- 'data' => $data,
- 'name' => Util::ucFirst(Lang::guide('guides')),
- 'hiddenCols' => $hCols,
- 'visibleCols' => $vCols,
- 'extraCols' => $xCols
- );
-
- $this->lvTabs[] = [GuideList::$brickFile, $tabData];
-
- $this->redButtons = [BUTTON_GUIDE_NEW => User::$id && User::canComment()];
- }
-
- protected function generateTitle()
- {
- array_unshift($this->title, $this->name);
- if (isset($this->category[0]))
- array_unshift($this->title, Lang::guide('category', $this->category[0]));
-
- }
-
- protected function generatePath()
- {
- if (isset($this->category[0]))
- $this->path[] = $this->category[0];
- }
-}
-
-?>
diff --git a/pages/guild.php b/pages/guild.php
deleted file mode 100644
index 25bd3490..00000000
--- a/pages/guild.php
+++ /dev/null
@@ -1,148 +0,0 @@
-error();
-
- $params = array_map('urldecode', explode('.', $pageParam));
- if ($params[0])
- $params[0] = Profiler::urlize($params[0]);
- if (isset($params[1]))
- $params[1] = Profiler::urlize($params[1]);
-
- parent::__construct($pageCall, $pageParam);
-
- if (count($params) == 1 && intval($params[0]))
- {
- $this->subject = new LocalGuildList(array(['g.id', intval($params[0])]));
- if ($this->subject->error)
- $this->notFound();
-
- header('Location: '.$this->subject->getProfileUrl(), true, 302);
- }
- else if (count($params) == 3)
- {
- $this->getSubjectFromUrl($pageParam);
- if (!$this->subjectName)
- $this->notFound();
-
- // 3 possibilities
- // 1) already synced to aowow
- if ($subject = DB::Aowow()->selectRow('SELECT id, realmGUID, cuFlags FROM ?_profiler_guild WHERE realm = ?d AND nameUrl = ?', $this->realmId, Profiler::urlize($this->subjectName)))
- {
- if ($subject['cuFlags'] & PROFILER_CU_NEEDS_RESYNC)
- {
- $this->handleIncompleteData($subject['realmGUID']);
- return;
- }
-
- $this->subjectGUID = $subject['id'];
- $this->subject = new LocalGuildList(array(['id', $subject['id']]));
- if ($this->subject->error)
- $this->notFound();
-
- $this->profile = $params;
- $this->name = sprintf(Lang::profiler('guildRoster'), $this->subject->getField('name'));
- }
- // 2) not yet synced but exists on realm (wont work if we get passed an urlized name, but there is nothing we can do about it)
- else if ($team = DB::Characters($this->realmId)->selectRow('SELECT guildid AS realmGUID, name FROM guild WHERE name = ?', Util::ucFirst($this->subjectName)))
- {
- $team['realm'] = $this->realmId;
- $team['cuFlags'] = PROFILER_CU_NEEDS_RESYNC;
-
- // create entry from realm with basic info
- DB::Aowow()->query('INSERT IGNORE INTO ?_profiler_guild (?#) VALUES (?a)', array_keys($team), array_values($team));
-
- $this->handleIncompleteData($team['realmGUID']);
- }
- // 3) does not exist at all
- else
- $this->notFound();
- }
- else
- $this->notFound();
- }
-
- protected function generateTitle()
- {
- $team = !empty($this->subject) ? $this->subject->getField('name') : $this->subjectName;
- $team .= ' ('.$this->realm.' - '.Lang::profiler('regions', $this->region).')';
-
- array_unshift($this->title, $team, Util::ucFirst(Lang::profiler('profiler')));
- }
-
- protected function generateContent()
- {
- if ($this->doResync)
- return;
-
- $this->addScript([JS_FILE, '?data=realms.weight-presets&locale='.User::$localeId.'&t='.$_SESSION['dataKey']]);
-
- $this->redButtons[BUTTON_RESYNC] = [$this->subjectGUID, 'guild'];
-
- /****************/
- /* Main Content */
- /****************/
-
-
- // statistic calculations here
-
- // smuggle the guild ranks into the html
- if ($ranks = DB::Aowow()->selectCol('SELECT `rank` AS ARRAY_KEY, name FROM ?_profiler_guild_rank WHERE guildId = ?d', $this->subjectGUID))
- $this->extraHTML = '';
-
-
- /**************/
- /* Extra Tabs */
- /**************/
-
- // tab: members
- $member = new LocalProfileList(array(['p.guild', $this->subjectGUID], CFG_SQL_LIMIT_NONE));
- if (!$member->error)
- {
- $this->lvTabs[] = ['profile', array(
- 'data' => array_values($member->getListviewData(PROFILEINFO_CHARACTER | PROFILEINFO_ARENA)),
- 'sort' => [-15],
- 'visibleCols' => ['race', 'classs', 'level', 'talents', 'gearscore', 'achievementpoints', 'guildrank'],
- 'hiddenCols' => ['guild', 'location']
- )];
- }
- }
-
- public function notFound(string $title = '', string $msg = '') : void
- {
- parent::notFound($title ?: Util::ucFirst(Lang::profiler('profiler')), $msg ?: Lang::profiler('notFound', 'guild'));
- }
-
- private function handleIncompleteData($teamGuid)
- {
- //display empty page and queue status
- $newId = Profiler::scheduleResync(Type::GUILD, $this->realmId, $teamGuid);
-
- $this->doResync = ['guild', $newId];
- $this->initialSync();
- }
-}
-
-?>
diff --git a/pages/guilds.php b/pages/guilds.php
deleted file mode 100644
index a13d5256..00000000
--- a/pages/guilds.php
+++ /dev/null
@@ -1,123 +0,0 @@
- ['filter' => FILTER_UNSAFE_RAW]];
-
- public function __construct($pageCall, $pageParam)
- {
- if (!CFG_PROFILER_ENABLE)
- $this->error();
-
- $this->getSubjectFromUrl($pageParam);
-
- $this->filterObj = new GuildListFilter();
-
- foreach (Profiler::getRealms() as $idx => $r)
- {
- if ($this->region && $r['region'] != $this->region)
- continue;
-
- if ($this->realm && $r['name'] != $this->realm)
- continue;
-
- $this->sumSubjects += DB::Characters($idx)->selectCell('SELECT COUNT(*) FROM guild');
- }
-
- parent::__construct($pageCall, $pageParam);
-
- $this->name = Lang::profiler('guilds');
- $this->subCat = $pageParam ? '='.$pageParam : '';
- }
-
- protected function generateTitle()
- {
- if ($this->realm)
- array_unshift($this->title, $this->realm,/* CFG_BATTLEGROUP,*/ Lang::profiler('regions', $this->region), Lang::profiler('guilds'));
- else if ($this->region)
- array_unshift($this->title, Lang::profiler('regions', $this->region), Lang::profiler('guilds'));
- else
- array_unshift($this->title, Lang::profiler('guilds'));
- }
-
- protected function generateContent()
- {
- $this->addScript([JS_FILE, '?data=realms&locale='.User::$localeId.'&t='.$_SESSION['dataKey']]);
-
- $conditions = array(
- ['c.deleteInfos_Account', null],
- ['c.level', MAX_LEVEL, '<='], // prevents JS errors
- [['c.extra_flags', Profiler::CHAR_GMFLAGS, '&'], 0]
- );
- if ($_ = $this->filterObj->getConditions())
- $conditions[] = $_;
-
- // recreate form selection
- $this->filter = $this->filterObj->getForm();
- $this->filter['query'] = $this->_get['filter'];
- $this->filter['initData'] = ['type' => 'guilds'];
-
- $tabData = array(
- 'id' => 'guilds',
- 'hideCount' => 1,
- 'sort' => [-3],
- 'visibleCols' => ['members', 'achievementpoints', 'gearscore'],
- 'hiddenCols' => ['guild'],
- );
-
- $miscParams = [];
- if ($this->realm)
- $miscParams['sv'] = $this->realm;
- if ($this->region)
- $miscParams['rg'] = $this->region;
-
- $guilds = new RemoteGuildList($conditions, $miscParams);
- if (!$guilds->error)
- {
- $guilds->initializeLocalEntries();
-
- $dFields = $guilds->hasDiffFields(['faction', 'type']);
- if (!($dFields & 0x1))
- $tabData['hiddenCols'][] = 'faction';
-
- if (($dFields & 0x2))
- $tabData['visibleCols'][] = 'size';
-
- $tabData['data'] = array_values($guilds->getListviewData());
-
- // create note if search limit was exceeded
- if ($this->filter['query'] && $guilds->getMatches() > CFG_SQL_LIMIT_DEFAULT)
- {
- $tabData['note'] = sprintf(Util::$tryFilteringString, 'LANG.lvnote_guildsfound2', $this->sumSubjects, $guilds->getMatches());
- $tabData['_truncated'] = 1;
- }
- else if ($guilds->getMatches() > CFG_SQL_LIMIT_DEFAULT)
- $tabData['note'] = sprintf(Util::$tryFilteringString, 'LANG.lvnote_guildsfound', $this->sumSubjects, 0);
-
- if ($this->filterObj->error)
- $tabData['_errors'] = 1;
- }
-
- $this->lvTabs[] = ['profile', $tabData, 'membersCol'];
-
- Lang::sort('game', 'cl');
- Lang::sort('game', 'ra');
- }
-}
-
-?>
diff --git a/pages/home.php b/pages/home.php
deleted file mode 100644
index 36b0f4f7..00000000
--- a/pages/home.php
+++ /dev/null
@@ -1,63 +0,0 @@
-addScript([CSS_STRING, '.announcement { margin: auto; max-width: 1200px; padding: 0px 15px 15px 15px }']);
-
- // load oneliner
- if ($_ = DB::Aowow()->selectRow('SELECT * FROM ?_home_oneliner WHERE active = 1 LIMIT 1'))
- $this->oneliner = Util::jsEscape(Util::localizedString($_, 'text'));
-
- // load featuredBox (user web server time)
- $this->featuredBox = DB::Aowow()->selectRow('SELECT id as ARRAY_KEY, n.* FROM ?_home_featuredbox n WHERE ?d BETWEEN startDate AND endDate ORDER BY id DESC LIMIT 1', time());
- if (!$this->featuredBox)
- return;
-
- $this->featuredBox = Util::defStatic($this->featuredBox);
-
- $this->featuredBox['text'] = Util::localizedString($this->featuredBox, 'text', true);
-
- if ($_ = (new Markup($this->featuredBox['text']))->parseGlobalsFromText())
- $this->extendGlobalData($_);
-
- if (empty($this->featuredBox['boxBG']))
- $this->featuredBox['boxBG'] = STATIC_URL.'/images/'.User::$localeString.'/mainpage-bg-news.jpg';
-
- // load overlay links
- $this->featuredBox['overlays'] = DB::Aowow()->select('SELECT * FROM ?_home_featuredbox_overlay WHERE featureId = ?d', $this->featuredBox['id']);
- foreach ($this->featuredBox['overlays'] as &$o)
- {
- $o['title'] = Util::localizedString($o, 'title', true);
- $o['title'] = Util::defStatic($o['title']);
- }
- }
-
- protected function generateTitle()
- {
- if ($_ = DB::Aowow()->selectCell('SELECT title FROM ?_home_titles WHERE active = 1 AND locale = ?d ORDER BY RAND() LIMIT 1', User::$localeId))
- $this->homeTitle = CFG_NAME.Lang::main('colon').$_;
- }
-
- protected function generatePath() {}
-}
-
-?>
diff --git a/pages/icon.php b/pages/icon.php
deleted file mode 100644
index 0188603f..00000000
--- a/pages/icon.php
+++ /dev/null
@@ -1,116 +0,0 @@
-typeId = intVal($id);
-
- $this->subject = new IconList(array(['id', $this->typeId]));
- if ($this->subject->error)
- $this->notFound(Lang::game('icon'), Lang::icon('notFound'));
-
- $this->extendGlobalData($this->subject->getJSGlobals());
-
- $this->name = $this->subject->getField('name');
- $this->icon = $this->subject->getField('name', true, true);
- }
-
- protected function generateContent()
- {
- /****************/
- /* Main Content */
- /****************/
-
- $this->redButtons = array(
- BUTTON_LINKS => ['type' => $this->type, 'typeId' => $this->typeId],
- BUTTON_WOWHEAD => false
- );
-
-
- /**************/
- /* Extra Tabs */
- /**************/
-
- // used by: spell
- $ubSpells = new SpellList(array(['iconId', $this->typeId]));
- if (!$ubSpells->error)
- {
- $this->extendGlobalData($ubSpells->getJsGlobals());
- $this->lvTabs[] = [SpellList::$brickFile, array(
- 'data' => array_values($ubSpells->getListviewData()),
- 'id' => 'used-by-spell'
- )];
- }
-
- // used by: item
- $ubItems = new ItemList(array(['iconId', $this->typeId]));
- if (!$ubItems->error)
- {
- $this->extendGlobalData($ubItems->getJsGlobals());
- $this->lvTabs[] = [ItemList::$brickFile, array(
- 'data' => array_values($ubItems->getListviewData()),
- 'id' => 'used-by-item'
- )];
- }
-
- // used by: achievement
- $ubAchievements = new AchievementList(array(['iconId', $this->typeId]));
- if (!$ubAchievements->error)
- {
- $this->extendGlobalData($ubAchievements->getJsGlobals());
- $this->lvTabs[] = [AchievementList::$brickFile, array(
- 'data' => array_values($ubAchievements->getListviewData()),
- 'id' => 'used-by-achievement'
- )];
- }
-
- // used by: currency
- $ubCurrencies = new CurrencyList(array(['iconId', $this->typeId]));
- if (!$ubCurrencies->error)
- {
- $this->extendGlobalData($ubCurrencies->getJsGlobals());
- $this->lvTabs[] = [CurrencyList::$brickFile, array(
- 'data' => array_values($ubCurrencies->getListviewData()),
- 'id' => 'used-by-currency'
- )];
- }
-
- // used by: hunter pet
- $ubPets = new PetList(array(['iconId', $this->typeId]));
- if (!$ubPets->error)
- {
- $this->extendGlobalData($ubPets->getJsGlobals());
- $this->lvTabs[] = [PetList::$brickFile, array(
- 'data' => array_values($ubPets->getListviewData()),
- 'id' => 'used-by-pet'
- )];
- }
- }
-
- protected function generateTitle()
- {
- array_unshift($this->title, $this->name, Util::ucFirst(Lang::game('icon')));
- }
-
- protected function generatePath() { }
-}
-
-?>
diff --git a/pages/icons.php b/pages/icons.php
deleted file mode 100644
index de087fe6..00000000
--- a/pages/icons.php
+++ /dev/null
@@ -1,112 +0,0 @@
- ['filter' => FILTER_UNSAFE_RAW]];
-
- public function __construct($pageCall)
- {
- $this->filterObj = new IconListFilter();
-
- parent::__construct($pageCall);
-
- $this->name = Util::ucFirst(Lang::game('icons'));
- }
-
- protected function generateContent()
- {
- $tabData = array(
- 'data' => [],
- );
-
- $sqlLimit = 600; // fits better onto the grid
-
- $conditions = [$sqlLimit];
-
- if (!User::isInGroup(U_GROUP_EMPLOYEE))
- $conditions[] = [['cuFlags', CUSTOM_EXCLUDE_FOR_LISTVIEW, '&'], 0];
-
- if ($_ = $this->filterObj->getConditions())
- $conditions[] = $_;
-
- $icons = new IconList($conditions);
-
- $tabData['data'] = array_values($icons->getListviewData());
- $this->extendGlobalData($icons->getJSGlobals());
-
- // recreate form selection
- $this->filter = $this->filterObj->getForm();
- $this->filter['query'] = $this->_get['filter'];
- $this->filter['initData'] = ['init' => 'icons'];
-
- if ($x = $this->filterObj->getSetCriteria())
- $this->filter['initData']['sc'] = $x;
-
- if ($icons->getMatches() > $sqlLimit)
- {
- $tabData['note'] = sprintf(Util::$tryFilteringEntityString, $icons->getMatches(), 'LANG.types[29][3]', $sqlLimit);
- $tabData['_truncated'] = 1;
- }
-
- if ($this->filterObj->error)
- $tabData['_errors'] = 1;
-
- $this->lvTabs[] = ['icongallery', $tabData];
- }
-
- protected function generateTitle()
- {
- $setCrt = $this->filterObj->getSetCriteria();
- $title = $this->name;
- if (isset($setCrt['cr']) && count($setCrt['cr']) == 1)
- {
- switch ($setCrt['cr'][0])
- {
- case 1:
- $title = Util::ucFirst(Lang::game('item')).' '.$title;
- break;
- case 2:
- $title = Util::ucFirst(Lang::game('spell')).' '.$title;
- break;
- case 3:
- $title = Util::ucFirst(Lang::game('achievement')).' '.$title;
- break;
- case 6:
- $title = Util::ucFirst(Lang::game('currency')).' '.$title;
- break;
- case 9:
- $title = Util::ucFirst(Lang::game('pet')).' '.$title;
- break;
- case 11:
- $title = Util::ucFirst(Lang::game('class')).' '.$title;
- break;
- }
- }
-
- array_unshift($this->title, $title);
- }
-
- protected function generatePath()
- {
- $setCrt = $this->filterObj->getSetCriteria();
- if (isset($setCrt['cr']) && count($setCrt['cr']) == 1)
- $this->path[] = $setCrt['cr'][0];
- }
-}
-
-?>
diff --git a/pages/itemset.php b/pages/itemset.php
deleted file mode 100644
index bcd06555..00000000
--- a/pages/itemset.php
+++ /dev/null
@@ -1,250 +0,0 @@
- ['filter' => FILTER_CALLBACK, 'options' => 'GenericPage::checkDomain']];
-
- private $powerTpl = '$WowheadPower.registerItemSet(%d, %d, %s);';
-
- public function __construct($pageCall, $id)
- {
- parent::__construct($pageCall, $id);
-
- // temp locale
- if ($this->mode == CACHE_TYPE_TOOLTIP && $this->_get['domain'])
- Util::powerUseLocale($this->_get['domain']);
-
- $this->typeId = intVal($id);
-
- $this->subject = new ItemsetList(array(['id', $this->typeId]));
- if ($this->subject->error)
- $this->notFound(Lang::game('itemset'), Lang::itemset('notFound'));
-
- $this->name = $this->subject->getField('name', true);
- $this->extendGlobalData($this->subject->getJSGlobals());
- }
-
- protected function generatePath()
- {
- if ($_ = $this->subject->getField('classMask'))
- {
- $bit = log($_, 2);
- if (intVal($bit) != $bit) // bit is float => multiple classes were set => skip out
- return;
-
- $this->path[] = $bit + 1;
- }
- }
-
- protected function generateTitle()
- {
- array_unshift($this->title, $this->name, Util::ucFirst(Lang::game('itemset')));
- }
-
- protected function generateContent()
- {
- $_ta = $this->subject->getField('contentGroup');
- $_ty = $this->subject->getField('type');
- $_cnt = count($this->subject->getField('pieces'));
-
- /***********/
- /* Infobox */
- /***********/
-
- $infobox = Lang::getInfoBoxForFlags($this->subject->getField('cuFlags'));
-
- // unavailable (todo (low): set data)
- if ($this->subject->getField('cuFlags') & CUSTOM_UNAVAILABLE)
- $infobox[] = Lang::main('unavailable');
-
- // worldevent
- if ($e = $this->subject->getField('eventId'))
- {
- $infobox[] = Lang::game('eventShort').Lang::main('colon').'[event='.$e.']';
- $this->extendGlobalIds(Type::WORLDEVENT, $e);
- }
-
- // itemLevel
- if ($min = $this->subject->getField('minLevel'))
- {
- $foo = Lang::game('level').Lang::main('colon').$min;
- $max = $this->subject->getField('maxLevel');
-
- if ($min < $max)
- $foo .= ' - '.$max;
-
- $infobox[] = $foo;
- }
-
- // class
- $jsg = [];
- if ($cl = Lang::getClassString($this->subject->getField('classMask'), $jsg, false))
- {
- $this->extendGlobalIds(Type::CHR_CLASS, ...$jsg);
- $t = count($jsg)== 1 ? Lang::game('class') : Lang::game('classes');
- $infobox[] = Util::ucFirst($t).Lang::main('colon').$cl;
- }
-
- // required level
- if ($lvl = $this->subject->getField('reqLevel'))
- $infobox[] = sprintf(Lang::game('reqLevel'), $lvl);
-
- // type
- if ($_ty)
- $infobox[] = Lang::game('type').Lang::main('colon').Lang::itemset('types', $_ty);
-
- // tag
- if ($_ta)
- $infobox[] = Lang::itemset('_tag').Lang::main('colon').'[url=?itemsets&filter=ta='.$_ta.']'.Lang::itemset('notes', $_ta).'[/url]';
-
- /****************/
- /* Main Content */
- /****************/
-
- // pieces + Summary
- $pieces = [];
- $eqList = [];
- $compare = [];
-
- if (!$this->subject->pieceToSet)
- $cnd = [0];
- else
- $cnd = ['i.id', array_keys($this->subject->pieceToSet)];
-
- $iList = new ItemList(array($cnd));
- $data = $iList->getListviewData(ITEMINFO_SUBITEMS | ITEMINFO_JSON);
- foreach ($iList->iterate() as $itemId => $__)
- {
- if (empty($data[$itemId]))
- continue;
-
- $slot = $iList->getField('slot');
- $disp = $iList->getField('displayId');
- if ($slot && $disp)
- $eqList[] = [$slot, $disp];
-
- $compare[] = $itemId;
-
- $pieces[$itemId] = array(
- 'name_'.User::$localeString => $iList->getField('name', true),
- 'quality' => $iList->getField('quality'),
- 'icon' => $iList->getField('iconString'),
- 'jsonequip' => $data[$itemId]
- );
- }
-
- $skill = '';
- if ($_sk = $this->subject->getField('skillId'))
- {
- $spellLink = sprintf('
%s (%s)', $_sk, Lang::spell('cat', 11, $_sk, 0), $this->subject->getField('skillLevel'));
- $skill = ' –
'.sprintf(Lang::game('requires'), $spellLink).'';
- }
-
- $this->bonusExt = $skill;
- $this->description = $_ta ? sprintf(Lang::itemset('_desc'), $this->name, Lang::itemset('notes', $_ta), $_cnt) : sprintf(Lang::itemset('_descTagless'), $this->name, $_cnt);
- $this->unavailable = $this->subject->getField('cuFlags') & CUSTOM_UNAVAILABLE;
- $this->infobox = $infobox ? '[ul][li]'.implode('[/li][li]', $infobox).'[/li][/ul]' : null;
- $this->pieces = $pieces;
- $this->spells = $this->subject->getBonuses();
- $this->expansion = 0;
- $this->redButtons = array(
- BUTTON_WOWHEAD => $this->typeId > 0, // bool only
- BUTTON_LINKS => ['type' => $this->type, 'typeId' => $this->typeId],
- BUTTON_VIEW3D => ['type' => Type::ITEMSET, 'typeId' => $this->typeId, 'equipList' => $eqList],
- BUTTON_COMPARE => ['eqList' => implode(':', $compare), 'qty' => $_cnt]
- );
- $this->summary = array(
- 'id' => 'itemset',
- 'template' => 'itemset',
- 'parent' => 'summary-generic',
- 'groups' => array_map(function ($v) { return [[$v]]; }, $compare),
- 'level' => $this->subject->getField('reqLevel'),
- );
-
- /**************/
- /* Extra Tabs */
- /**************/
-
- // related sets (priority: 1: similar tag + class; 2: has event; 3: no tag + similar type, 4: similar type + profession)
- $rel = [];
-
- if ($_ta && count($this->path) == 3)
- {
- $rel[] = ['id', $this->typeId, '!'];
- $rel[] = ['classMask', 1 << (end($this->path) - 1), '&'];
- $rel[] = ['contentGroup', (int)$_ta];
- }
- else if ($this->subject->getField('eventId'))
- {
- $rel[] = ['id', $this->typeId, '!'];
- $rel[] = ['eventId', 0, '!'];
- }
- else if ($this->subject->getField('skillId'))
- {
- $rel[] = ['id', $this->typeId, '!'];
- $rel[] = ['contentGroup', 0];
- $rel[] = ['skillId', 0, '!'];
- $rel[] = ['type', $_ty];
- }
- else if (!$_ta && $_ty)
- {
- $rel[] = ['id', $this->typeId, '!'];
- $rel[] = ['contentGroup', 0];
- $rel[] = ['type', $_ty];
- $rel[] = ['skillId', 0];
- }
-
- if ($rel)
- {
- $relSets = new ItemsetList($rel);
- if (!$relSets->error)
- {
- $tabData = array(
- 'data' => array_values($relSets->getListviewData()),
- 'id' => 'see-also',
- 'name' => '$LANG.tab_seealso'
- );
-
- if (!$relSets->hasDiffFields(['classMask']))
- $tabData['hiddenCols'] = ['classes'];
-
- $this->lvTabs[] = ['itemset', $tabData];
-
- $this->extendGlobalData($relSets->getJSGlobals());
- }
- }
- }
-
- protected function generateTooltip()
- {
- $power = new StdClass();
- if (!$this->subject->error)
- {
- $power->{'name_'.User::$localeString} = $this->subject->getField('name', true);
- $power->{'tooltip_'.User::$localeString} = $this->subject->renderTooltip();
- }
-
- return sprintf($this->powerTpl, $this->typeId, User::$localeId, Util::toJSON($power, JSON_AOWOW_POWER));
- }
-}
-
-
-
-
-?>
diff --git a/pages/itemsets.php b/pages/itemsets.php
deleted file mode 100644
index 67b5c03f..00000000
--- a/pages/itemsets.php
+++ /dev/null
@@ -1,98 +0,0 @@
- ['filter' => FILTER_UNSAFE_RAW]];
-
- public function __construct($pageCall, $pageParam)
- {
- $this->getCategoryFromUrl($pageParam);
- $this->filterObj = new ItemsetListFilter(false, ['parentCats' => $this->category]);
-
- parent::__construct($pageCall, $pageParam);
-
- $this->name = Util::ucFirst(Lang::game('itemsets'));
- }
-
- protected function generateContent()
- {
- $this->addScript([JS_FILE, '?data=weight-presets&locale='.User::$localeId.'&t='.$_SESSION['dataKey']]);
-
- $conditions = [];
-
- if (!User::isInGroup(U_GROUP_EMPLOYEE))
- $conditions[] = [['cuFlags', CUSTOM_EXCLUDE_FOR_LISTVIEW, '&'], 0];
-
- if ($_ = $this->filterObj->getConditions())
- $conditions[] = $_;
-
- $itemsets = new ItemsetList($conditions);
- $this->extendGlobalData($itemsets->getJSGlobals());
-
- // recreate form selection
- $this->filter = $this->filterObj->getForm();
- $this->filter['query'] = $this->_get['filter'];
- $this->filter['initData'] = ['init' => 'itemsets'];
-
- if ($x = $this->filterObj->getSetCriteria())
- $this->filter['initData']['sc'] = $x;
-
- $xCols = $this->filterObj->getExtraCols();
- if ($xCols)
- $this->filter['initData']['ec'] = $xCols;
-
- $tabData = ['data' => array_values($itemsets->getListviewData())];
-
- if ($xCols)
- $tabData['extraCols'] = '$fi_getExtraCols(fi_extraCols, 0, 0)';
-
- // create note if search limit was exceeded
- if ($itemsets->getMatches() > CFG_SQL_LIMIT_DEFAULT)
- {
- $tabData['note'] = sprintf(Util::$tryFilteringString, 'LANG.lvnote_itemsetsfound', $itemsets->getMatches(), CFG_SQL_LIMIT_DEFAULT);
- $tabData['_truncated'] = 1;
- }
-
- if ($this->filterObj->error)
- $tabData['_errors'] = 1;
-
- $this->lvTabs[] = ['itemset', $tabData];
-
- // sort for dropdown-menus
- Lang::sort('itemset', 'notes', SORT_NATURAL);
- Lang::sort('game', 'si');
- }
-
- protected function generateTitle()
- {
- array_unshift($this->title, $this->name);
-
- $form = $this->filterObj->getForm('form');
- if (isset($form['cl']))
- array_unshift($this->title, Lang::game('cl', $form['cl']));
- }
-
- protected function generatePath()
- {
- $form = $this->filterObj->getForm('form');
- if (isset($form['cl']))
- $this->path[] = $form['cl'];
- }
-}
-
-?>
diff --git a/pages/mail.php b/pages/mail.php
deleted file mode 100644
index 0a7d2e95..00000000
--- a/pages/mail.php
+++ /dev/null
@@ -1,167 +0,0 @@
-typeId = intVal($id);
-
- $this->subject = new MailList(array(['id', $this->typeId]));
-
- if ($this->subject->error)
- $this->notFound(lang::game('mail'), Lang::mail('notFound'));
-
- $this->extendGlobalData($this->subject->getJSGlobals());
-
- $this->name = Util::htmlEscape(Util::ucFirst($this->subject->getField('name', true)));
- }
-
- protected function generateContent()
- {
- /***********/
- /* Infobox */
- /***********/
-
- $infobox = [];
-
- // sender + delay
- if ($this->typeId < 0) // def. achievement
- {
- if ($npcId = DB::World()->selectCell('SELECT Sender FROM achievement_reward WHERE ID = ?d', -$this->typeId))
- {
- $infobox[] = Lang::mail('sender').Lang::main('colon').'[npc='.$npcId.']';
- $this->extendGlobalIds(Type::NPC, $npcId);
- }
- }
- else if ($mlr = DB::World()->selectRow('SELECT * FROM mail_level_reward WHERE mailTemplateId = ?d', $this->typeId)) // level rewards
- {
- if ($mlr['level'])
- $infobox[] = Lang::game('level').Lang::main('colon').$mlr['level'];
-
- $rIds = [];
- if ($r = Lang::getRaceString($mlr['raceMask'], $rIds, false))
- {
- $infobox[] = Lang::game('races').Lang::main('colon').$r;
- $this->extendGlobalIds(Type::CHR_RACE, ...$rIds);
- }
-
- $infobox[] = Lang::mail('sender').Lang::main('colon').'[npc='.$mlr['senderEntry'].']';
- $this->extendGlobalIds(Type::NPC, $mlr['senderEntry']);
- }
- else // achievement or quest
- {
- if ($q = DB::Aowow()->selectRow('SELECT id, rewardMailDelay FROM ?_quests WHERE rewardMailTemplateId = ?d', $this->typeId))
- {
- if ($npcId= DB::World()->selectCell('SELECT RewardMailSenderEntry FROM quest_mail_sender WHERE QuestId = ?d', $q['id']))
- {
- $infobox[] = Lang::mail('sender').Lang::main('colon').'[npc='.$npcId.']';
- $this->extendGlobalIds(Type::NPC, $npcId);
- }
- else if ($npcId = DB::Aowow()->selectCell('SELECT typeId FROM ?_quests_startend WHERE questId = ?d AND type = ?d AND method & ?d', $q['id'], Type::NPC, 0x2))
- {
- $infobox[] = Lang::mail('sender').Lang::main('colon').'[npc='.$npcId.']';
- $this->extendGlobalIds(Type::NPC, $npcId);
- }
-
- if ($q['rewardMailDelay'] > 0)
- $infobox[] = Lang::mail('delay').Lang::main('colon').''.Util::formatTime($q['rewardMailDelay'] * 1000);
- }
- else if ($npcId = DB::World()->selectCell('SELECT Sender FROM achievement_reward WHERE MailTemplateId = ?d', $this->typeId))
- {
- $infobox[] = Lang::mail('sender').Lang::main('colon').'[npc='.$npcId.']';
- $this->extendGlobalIds(Type::NPC, $npcId);
- }
- }
-
- /****************/
- /* Main Content */
- /****************/
-
- $this->infobox = $infobox ? '[ul][li]'.implode('[/li][li]', $infobox).'[/li][/ul]' : '';
- $this->redButtons = array(
- BUTTON_LINKS => ['type' => $this->type, 'typeId' => $this->typeId],
- BUTTON_WOWHEAD => false
- );
-
- $this->extraText = Util::parseHtmlText($this->subject->getField('text', true), true);
-
-
- /**************/
- /* Extra Tabs */
- /**************/
-
- // tab: attachment
- if ($itemId = $this->subject->getField('attachment'))
- {
- $attachment = new ItemList(array(['id', $itemId]));
- if (!$attachment->error)
- {
- $this->extendGlobalData($attachment->getJsGlobals());
- $this->lvTabs[] = [ItemList::$brickFile, array(
- 'data' => array_values($attachment->getListviewData()),
- 'name' => Lang::mail('attachment'),
- 'id' => 'attachment'
- )];
- }
- }
-
-
- if ($this->typeId < 0 || // used by: achievement
- ($acvId = DB::World()->selectCell('SELECT ID FROM achievement_reward WHERE MailTemplateId = ?d', $this->typeId)))
- {
- $ubAchievements = new AchievementList(array(['id', $this->typeId < 0 ? -$this->typeId : $acvId]));
- if (!$ubAchievements->error)
- {
- $this->extendGlobalData($ubAchievements->getJsGlobals());
- $this->lvTabs[] = [AchievementList::$brickFile, array(
- 'data' => array_values($ubAchievements->getListviewData()),
- 'id' => 'used-by-achievement'
- )];
- }
- }
- else if ($npcId = DB::World()->selectCell('SELECT ID FROM achievement_reward WHERE MailTemplateId = ?d', $this->typeId))
- {
- $infobox[] = '[Sender]: [npc='.$npcId.']';
- $this->extendGlobalIds(Type::NPC, $npcId);
- }
-
- else // used by: quest
- {
- $ubQuests = new QuestList(array(['rewardMailTemplateId', $this->typeId]));
- if (!$ubQuests->error)
- {
- $this->extendGlobalData($ubQuests->getJsGlobals());
- $this->lvTabs[] = [QuestList::$brickFile, array(
- 'data' => array_values($ubQuests->getListviewData()),
- 'id' => 'used-by-quest'
- )];
- }
- }
- }
-
- protected function generateTitle()
- {
- array_unshift($this->title, Util::ucFirst($this->subject->getField('name', true)), Util::ucFirst(Lang::game('mail')));
- }
-
- protected function generatePath() { }
-}
-
-?>
diff --git a/pages/mails.php b/pages/mails.php
deleted file mode 100644
index 4a3e48f2..00000000
--- a/pages/mails.php
+++ /dev/null
@@ -1,46 +0,0 @@
-name = Util::ucFirst(Lang::game('mails'));
- }
-
- protected function generateContent()
- {
- $tabData = [];
- $mails = new MailList();
- if (!$mails->error)
- $tabData['data'] = array_values($mails->getListviewData());
-
- $this->extendGlobalData($mails->getJsGlobals());
-
- $this->lvTabs[] = ['mail', $tabData, 'mail'];
- }
-
- protected function generateTitle()
- {
- array_unshift($this->title, $this->name);
- }
-
- protected function generatePath() { }
-}
-
-?>
diff --git a/pages/maps.php b/pages/maps.php
deleted file mode 100644
index 2ab00810..00000000
--- a/pages/maps.php
+++ /dev/null
@@ -1,38 +0,0 @@
-name = Lang::maps('maps');
- }
-
- protected function generateContent()
- {
- // add conditional js
- $this->addScript([JS_FILE, '?data=zones&locale='.User::$localeId.'&t='.$_SESSION['dataKey']]);
- }
-
- protected function generateTitle()
- {
- array_unshift($this->title, $this->name);
- }
-
- protected function generatePath() {}
-}
-
-?>
diff --git a/pages/more.php b/pages/more.php
deleted file mode 100644
index ea1b8532..00000000
--- a/pages/more.php
+++ /dev/null
@@ -1,239 +0,0 @@
- CFG_REP_REQ_COMMENT, // write comments
- 2 => 0, // NYI post external links
- 4 => 0, // NYI no captcha
- 5 => CFG_REP_REQ_SUPERVOTE, // votes count for more
- 9 => CFG_REP_REQ_VOTEMORE_BASE, // more votes per day
- 10 => CFG_REP_REQ_UPVOTE, // can upvote
- 11 => CFG_REP_REQ_DOWNVOTE, // can downvote
- 12 => CFG_REP_REQ_REPLY, // can reply
- 13 => 0, // avatar border [NYI: checked by js, avatars not in use]
- 14 => 0, // avatar border [NYI: checked by js, avatars not in use]
- 15 => 0, // avatar border [NYI: checked by js, avatars not in use]
- 16 => 0, // avatar border [NYI: checked by js, avatars not in use]
- 17 => CFG_REP_REQ_PREMIUM // premium status
- );
-
- private $validPages = array( // [tabId, path[, subPaths]]
- 'whats-new' => [2, [2, 7]],
- 'searchbox' => [2, [2, 16]],
- 'tooltips' => [2, [2, 10]],
- 'faq' => [2, [2, 3]],
- 'aboutus' => [2, [2, 0]],
- 'searchplugins' => [2, [2, 8]],
- 'help' => [2, [2, 13], ['commenting-and-you', 'modelviewer', 'screenshots-tips-tricks', 'stat-weighting', 'talent-calculator', 'item-comparison', 'profiler', 'markup-guide']],
- 'reputation' => [1, [3, 10]],
- 'privilege' => [1, [3, 10], [1, 2, 4, 5, 9, 10, 11, 12, 13, 14, 15, 16, 17]],
- 'privileges' => [1, [3, 10, 0]],
- 'top-users' => [1, [3, 11]]
- );
-
- public function __construct($pageCall, $subPage)
- {
- parent::__construct($pageCall, $subPage);
-
- // chack if page is valid
- if (isset($this->validPages[$pageCall]))
- {
- $pageData = $this->validPages[$pageCall];
-
- $this->tab = $pageData[0];
- $this->path = $pageData[1];
- $this->page = [$pageCall, $subPage];
-
- if ($subPage && isset($pageData[2]))
- {
- $exists = array_search($subPage, $pageData[2]);
- if ($exists === false)
- $this->error();
-
- if (is_numeric($subPage))
- $this->articleUrl = $pageCall.'='.$subPage;
- else
- $this->articleUrl = $subPage;
-
- $this->path[] = $subPage;
- $this->name = Lang::main('moreTitles', $pageCall, $subPage);
- }
- else
- {
- $this->articleUrl = $pageCall;
- $this->name = Lang::main('moreTitles', $pageCall);
- }
- }
- else
- $this->error();
-
- // order by requirement ASC
- asort($this->req2priv);
- }
-
- protected function generateContent()
- {
- switch ($this->page[0])
- {
- case 'reputation':
- $this->handleReputationPage();
- return;
- case 'privileges':
- $this->handlePrivilegesPage();
- return;
- case 'privilege':
- $this->tpl = 'privilege';
- $this->privReqPoints = sprintf(Lang::privileges('reqPoints'), Lang::nf($this->req2priv[$this->page[1]]));
- return;
- case 'top-users':
- $this->handleTopUsersPage();
- return;
- default:
- return;
- }
- }
-
- protected function postArticle()
- {
- if ($this->page[0] != 'reputation' &&
- $this->page[0] != 'privileges' &&
- $this->page[0] != 'privilege')
- return;
-
- $txt = &$this->article['text'];
- $consts = get_defined_constants(true);
- foreach ($consts['user'] as $k => $v)
- {
- if (strstr($k, 'CFG_REP_'))
- $txt = str_replace($k, Lang::nf($v), $txt);
- else if ($k == 'CFG_USER_MAX_VOTES' || $k == 'CFG_BOARD_URL')
- $txt = str_replace($k, $v, $txt);
- }
- }
-
- protected function generatePath() { }
-
- protected function generateTitle()
- {
- array_unshift($this->title, $this->name);
- }
-
- private function handleReputationPage()
- {
- if (!User::$id)
- return;
-
- if ($repData = DB::Aowow()->select('SELECT action, amount, date AS \'when\', IF(action IN (3, 4, 5), sourceA, 0) AS param FROM ?_account_reputation WHERE userId = ?d', User::$id))
- {
- foreach ($repData as &$r)
- $r['when'] = date(Util::$dateFormatInternal, $r['when']);
-
- $this->tabsTitle = Lang::main('yourRepHistory');
- $this->forceTabs = true;
- $this->lvTabs[] = ['reputationhistory', array(
- 'id' => 'reputation-history',
- 'name' => '$LANG.reputationhistory',
- 'data' => $repData
- )];
- }
- }
-
- private function handlePrivilegesPage()
- {
- $this->tpl = 'privileges';
- $this->privileges = [];
-
- foreach ($this->req2priv as $id => $val)
- if ($val)
- $this->privileges[$id] = array(
- User::getReputation() >= $val,
- Lang::privileges('_privileges', $id),
- $val
- );
- }
-
- private function handleTopUsersPage()
- {
- $tabs = array(
- [0, 'top-users-alltime', '$LANG.alltime_stc' ],
- [time() - MONTH, 'top-users-monthly', '$LANG.lastmonth_stc'],
- [time() - WEEK, 'top-users-weekly', '$LANG.lastweek_stc' ]
- );
-
- $nullFields = array(
- 'uploads' => 0,
- 'posts' => 0,
- 'gold' => 0,
- 'silver' => 0,
- 'copper' => 0
- );
-
- foreach ($tabs as [$t, $tabId, $tabName])
- {
- // stuff received
- $res = DB::Aowow()->select('
- SELECT
- a.id AS ARRAY_KEY,
- a.displayName AS username,
- a.userGroups AS `groups`,
- a.joinDate AS creation,
- SUM(r.amount) AS reputation,
- SUM(IF(r.`action` = 3, 1, 0)) AS comments,
- SUM(IF(r.`action` = 6, 1, 0)) AS screenshots,
- SUM(IF(r.`action` = 9, 1, 0)) AS reports
- FROM ?_account_reputation r
- JOIN ?_account a ON a.id = r.userId
- {WHERE r.date > ?d}
- GROUP BY a.id
- ORDER BY reputation DESC
- LIMIT ?d
- ', $t ?: DBSIMPLE_SKIP, CFG_SQL_LIMIT_SEARCH);
-
- $data = [];
- if ($res)
- {
- // stuff given
- $votes = DB::Aowow()->selectCol(
- 'SELECT sourceB AS ARRAY_KEY, SUM(1) FROM ?_account_reputation WHERE action IN (4, 5) AND sourceB IN (?a) {AND date > ?d} GROUP BY sourceB',
- array_keys($res),
- $t ?: DBSIMPLE_SKIP
- );
- foreach ($res as $uId => &$r)
- {
- $r['creation'] = date('c', $r['creation']);
- $r['votes'] = empty($votes[$uId]) ? 0 : $votes[$uId];
- $r = array_merge($r, $nullFields);
- }
-
- $data = array_values($res);
- }
-
- $this->lvTabs[] = ['topusers', array(
- 'hiddenCols' => ['achievements', 'posts', 'uploads'],
- 'visibleCols' => ['created'],
- 'name' => '$LANG.lastweek_stc',
- 'name' => $tabName,
- 'id' => $tabId,
- 'data' => $data
- )];
- }
- }
-}
-
-?>
diff --git a/pages/npc.php b/pages/npc.php
deleted file mode 100644
index b94c4a59..00000000
--- a/pages/npc.php
+++ /dev/null
@@ -1,1071 +0,0 @@
- ['filter' => FILTER_CALLBACK, 'options' => 'GenericPage::checkDomain']];
-
- private $soundIds = [];
- private $powerTpl = '$WowheadPower.registerNpc(%d, %d, %s);';
-
- public function __construct($pageCall, $id)
- {
- parent::__construct($pageCall, $id);
-
- // temp locale
- if ($this->mode == CACHE_TYPE_TOOLTIP && $this->_get['domain'])
- Util::powerUseLocale($this->_get['domain']);
-
- $this->typeId = intVal($id);
-
- $this->subject = new CreatureList(array(['id', $this->typeId]));
- if ($this->subject->error)
- $this->notFound(Lang::game('npc'), Lang::npc('notFound'));
-
- $this->name = Util::htmlEscape($this->subject->getField('name', true));
- $this->subname = Util::htmlEscape($this->subject->getField('subname', true));
- }
-
- protected function generatePath()
- {
- $this->path[] = $this->subject->getField('type');
-
- if ($_ = $this->subject->getField('family'))
- $this->path[] = $_;
- }
-
- protected function generateTitle()
- {
- array_unshift($this->title, $this->subject->getField('name', true), Util::ucFirst(Lang::game('npc')));
- }
-
- protected function generateContent()
- {
- $this->addScript([JS_FILE, '?data=zones&locale='.User::$localeId.'&t='.$_SESSION['dataKey']]);
-
- $_typeFlags = $this->subject->getField('typeFlags');
- $_altIds = [];
- $_altNPCs = null;
- $placeholder = null;
- $accessory = [];
-
- // difficulty entries of self
- if ($this->subject->getField('cuFlags') & NPC_CU_DIFFICULTY_DUMMY)
- $placeholder = [$this->subject->getField('parentId'), $this->subject->getField('parent', true)];
- else
- {
- for ($i = 1; $i < 4; $i++)
- if ($_ = $this->subject->getField('difficultyEntry'.$i))
- $_altIds[$_] = $i;
-
- if ($_altIds)
- $_altNPCs = new CreatureList(array(['id', array_keys($_altIds)]));
- }
-
- if ($_ = DB::World()->selectCol('SELECT DISTINCT entry FROM vehicle_template_accessory WHERE accessory_entry = ?d', $this->typeId))
- {
- $vehicles = new CreatureList(array(['id', $_]));
- foreach ($vehicles->iterate() as $id => $__)
- $accessory[] = [$id, $vehicles->getField('name', true)];
- }
-
- // try to determine, if it's spawned in a dungeon or raid (shaky at best, if spawned by script)
- $mapType = 0;
- if ($maps = DB::Aowow()->selectCol('SELECT DISTINCT areaId from ?_spawns WHERE type = ?d AND typeId = ?d', Type::NPC, $this->typeId))
- {
- if (count($maps) == 1) // should only exist in one instance
- {
- switch (DB::Aowow()->selectCell('SELECT `type` FROM ?_zones WHERE id = ?d', $maps[0]))
- {
- case 2:
- case 5: $mapType = 1; break;
- case 3:
- case 7:
- case 8: $mapType = 2; break;
- }
- }
- }
- else if ($_altIds) // not spawned, but has difficultyDummies
- {
- if (count($_altIds) > 1) // 3 or more version -> definitly raid (10/25 + hc)
- $mapType = 2;
- else // 2 versions; may be Heroic (use this), but may also be 10/25-raid
- $mapType = 1;
- }
-
-
-
-
-
- /***********/
- /* Infobox */
- /***********/
-
- $infobox = Lang::getInfoBoxForFlags($this->subject->getField('cuFlags'));
-
- // Event (ignore events, where the object only gets removed)
- if ($_ = DB::World()->selectCol('SELECT DISTINCT ge.eventEntry FROM game_event ge, game_event_creature gec, creature c WHERE ge.eventEntry = gec.eventEntry AND c.guid = gec.guid AND c.id = ?d', $this->typeId))
- {
- $this->extendGlobalIds(Type::WORLDEVENT, ...$_);
- $ev = [];
- foreach ($_ as $i => $e)
- $ev[] = ($i % 2 ? '[br]' : ' ') . '[event='.$e.']';
-
- $infobox[] = Util::ucFirst(Lang::game('eventShort')).Lang::main('colon').implode(',', $ev);
- }
-
- // Level
- if ($this->subject->getField('rank') != NPC_RANK_BOSS)
- {
- $level = $this->subject->getField('minLevel');
- $maxLvl = $this->subject->getField('maxLevel');
- if ($level < $maxLvl)
- $level .= ' - '.$maxLvl;
- }
- else // Boss Level
- $level = '??';
-
- $infobox[] = Lang::game('level').Lang::main('colon').$level;
-
- // Classification
- if ($_ = $this->subject->getField('rank')) // != NPC_RANK_NORMAL
- {
- $str = $this->subject->isBoss() ? '[span class=icon-boss]'.Lang::npc('rank', $_).'[/span]' : Lang::npc('rank', $_);
- $infobox[] = Lang::npc('classification').Lang::main('colon').$str;
- }
-
- // Reaction
- $_ = function ($r)
- {
- if ($r == 1) return 2;
- if ($r == -1) return 10;
- return;
- };
- $infobox[] = Lang::npc('react').Lang::main('colon').'[color=q'.$_($this->subject->getField('A')).']A[/color] [color=q'.$_($this->subject->getField('H')).']H[/color]';
-
- // Faction
- $this->extendGlobalIds(Type::FACTION, $this->subject->getField('factionId'));
- $infobox[] = Util::ucFirst(Lang::game('faction')).Lang::main('colon').'[faction='.$this->subject->getField('factionId').']';
-
- // Tameable
- if ($_typeFlags & 0x1)
- if ($_ = $this->subject->getField('family'))
- $infobox[] = sprintf(Lang::npc('tameable'), '[url=pet='.$_.']'.Lang::game('fa', $_).'[/url]');
-
- // Wealth
- if ($_ = intVal(($this->subject->getField('minGold') + $this->subject->getField('maxGold')) / 2))
- $infobox[] = Lang::npc('worth').Lang::main('colon').'[tooltip=tooltip_avgmoneydropped][money='.$_.'][/tooltip]';
-
- // is Vehicle
- if ($this->subject->getField('vehicleId'))
- $infobox[] = Lang::npc('vehicle');
-
- if (User::isInGroup(U_GROUP_EMPLOYEE))
- {
- // AI
- if ($_ = $this->subject->getField('scriptName'))
- $infobox[] = 'Script'.Lang::main('colon').$_;
- else if ($_ = $this->subject->getField('aiName'))
- $infobox[] = 'AI'.Lang::main('colon').$_;
-
- // Mechanic immune
- if ($immuneMask = $this->subject->getField('mechanicImmuneMask'))
- {
- $buff = [];
- for ($i = 0; $i < 31; $i++)
- if ($immuneMask & (1 << $i))
- $buff[] = (!fMod(count($buff), 3) ? "\n" : null).'[url=?spells&filter=me='.($i + 1).']'.Lang::game('me', $i + 1).'[/url]';
-
- $infobox[] = 'Not affected by mechanic'.Lang::main('colon').implode(', ', $buff);
- }
-
- // extra flags
- if ($flagsExtra = $this->subject->getField('flagsExtra'))
- {
- $buff = [];
- if ($flagsExtra & 0x000001)
- $buff[] = 'Binds attacker to instance on death';
- if ($flagsExtra & 0x000002)
- $buff[] = "[tooltip name=civilian]- does not aggro\n- death costs Honor[/tooltip][span class=tip tooltip=civilian]Civilian[/span]";
- if ($flagsExtra & 0x000004)
- $buff[] = 'Cannot parry';
- if ($flagsExtra & 0x000008)
- $buff[] = 'Has no parry haste';
- if ($flagsExtra & 0x000010)
- $buff[] = 'Cannot block';
- if ($flagsExtra & 0x000020)
- $buff[] = 'Cannot deal Crushing Blows';
- if ($flagsExtra & 0x000040)
- $buff[] = 'Rewards no experience';
- if ($flagsExtra & 0x000080)
- $buff[] = 'Trigger creature';
- if ($flagsExtra & 0x000100)
- $buff[] = 'Immune to Taunt';
- if ($flagsExtra & 0x008000)
- $buff[] = "[tooltip name=guard]- engages PvP attackers\n- ignores enemy stealth, invisibility and Feign Death[/tooltip][span class=tip tooltip=guard]Guard[/span]";
- if ($flagsExtra & 0x020000)
- $buff[] = 'Cannot deal Critical Hits';
- if ($flagsExtra & 0x040000)
- $buff[] = 'Attacker does not gain weapon skill';
- if ($flagsExtra & 0x080000)
- $buff[] = 'Taunt has diminishing returns';
- if ($flagsExtra & 0x100000)
- $buff[] = 'Is subject to diminishing returns';
-
- if ($buff)
- $infobox[] = 'Extra Flags'.Lang::main('colon').'[ul][li]'.implode('[/li][li]', $buff).'[/li][/ul]';
- }
-
- // Mode dummy references
- if ($_altNPCs)
- {
- $this->extendGlobalData($_altNPCs->getJSGlobals());
- $buff = 'Difficulty Versions'.Lang::main('colon').'[ul]';
- foreach ($_altNPCs->iterate() as $id => $__)
- $buff .= '[li][npc='.$id.'][/li]';
- $infobox[] = $buff.'[/ul]';
- }
- }
-
- // > Stats
- $stats = [];
- $modes = []; // get difficulty versions if set
- $hint = '[tooltip name=%3$s][table cellspacing=10][tr]%1s[/tr][/table][/tooltip][span class=tip tooltip=%3$s]%2s[/span]';
- $modeRow = '[tr][td]%s [/td][td]%s[/td][/tr]';
- // Health
- $health = $this->subject->getBaseStats('health');
- $stats['health'] = Util::ucFirst(Lang::spell('powerTypes', -2)).Lang::main('colon').($health[0] < $health[1] ? Lang::nf($health[0]).' - '.Lang::nf($health[1]) : Lang::nf($health[0]));
-
- // Mana (may be 0)
- $mana = $this->subject->getBaseStats('power');
- $stats['mana'] = $mana[0] ? Lang::spell('powerTypes', 0).Lang::main('colon').($mana[0] < $mana[1] ? Lang::nf($mana[0]).' - '.Lang::nf($mana[1]) : Lang::nf($mana[0])) : null;
-
- // Armor
- $armor = $this->subject->getBaseStats('armor');
- $stats['armor'] = Lang::npc('armor').Lang::main('colon').($armor[0] < $armor[1] ? Lang::nf($armor[0]).' - '.Lang::nf($armor[1]) : Lang::nf($armor[0]));
-
- // Resistances
- $resNames = [null, 'hol', 'fir', 'nat', 'fro', 'sha', 'arc'];
- $tmpRes = [];
- $stats['resistance'] = '';
- foreach ($this->subject->getBaseStats('resistance') as $sc => $amt)
- if ($amt)
- $tmpRes[] = '[span class="moneyschool'.$resNames[$sc].'"]'.$amt.'[/span]';
-
- if ($tmpRes)
- {
- $stats['resistance'] = Lang::npc('resistances').Lang::main('colon');
- if (count($tmpRes) > 3)
- $stats['resistance'] .= implode(' ', array_slice($tmpRes, 0, 3)).'[br]'.implode(' ', array_slice($tmpRes, 3));
- else
- $stats['resistance'] .= implode(' ', $tmpRes);
- }
-
- // Melee Damage
- $melee = $this->subject->getBaseStats('melee');
- if ($_ = $this->subject->getField('dmgSchool')) // magic damage
- $stats['melee'] = Lang::npc('melee').Lang::main('colon').Lang::nf($melee[0]).' - '.Lang::nf($melee[1]).' ('.Lang::game('sc', $_).')';
- else // phys. damage
- $stats['melee'] = Lang::npc('melee').Lang::main('colon').Lang::nf($melee[0]).' - '.Lang::nf($melee[1]);
-
- // Ranged Damage
- $ranged = $this->subject->getBaseStats('ranged');
- $stats['ranged'] = Lang::npc('ranged').Lang::main('colon').Lang::nf($ranged[0]).' - '.Lang::nf($ranged[1]);
-
- if (in_array($mapType, [1, 2])) // Dungeon or Raid
- {
- foreach ($_altIds as $id => $mode)
- {
- foreach ($_altNPCs->iterate() as $dId => $__)
- {
- if ($dId != $id)
- continue;
-
- $m = Lang::npc('modes', $mapType, $mode);
-
- // Health
- $health = $_altNPCs->getBaseStats('health');
- $modes['health'][] = sprintf($modeRow, $m, $health[0] < $health[1] ? Lang::nf($health[0]).' - '.Lang::nf($health[1]) : Lang::nf($health[0]));
-
- // Mana (may be 0)
- $mana = $_altNPCs->getBaseStats('power');
- $modes['mana'][] = $mana[0] ? sprintf($modeRow, $m, $mana[0] < $mana[1] ? Lang::nf($mana[0]).' - '.Lang::nf($mana[1]) : Lang::nf($mana[0])) : null;
-
- // Armor
- $armor = $_altNPCs->getBaseStats('armor');
- $modes['armor'][] = sprintf($modeRow, $m, $armor[0] < $armor[1] ? Lang::nf($armor[0]).' - '.Lang::nf($armor[1]) : Lang::nf($armor[0]));
-
- // Resistances
- $tmpRes = '';
- foreach ($_altNPCs->getBaseStats('resistance') as $sc => $amt)
- $tmpRes .= '[td]'.$amt.'[/td]';
-
- if ($tmpRes)
- {
- if (!isset($modes['resistance'])) // init table head
- $modes['resistance'][] = '[td][/td][td][span class="moneyschoolhol"] [/span][/td][td][span class="moneyschoolfir"] [/span][/td][td][span class="moneyschoolnat"] [/span][/td][td][span class="moneyschoolfro"] [/span][/td][td][span class="moneyschoolsha"] [/span][/td][td][span class="moneyschoolarc"][/span][/td]';
-
- if (!$stats['resistance']) // base creature has no resistance. -> display list item.
- $stats['resistance'] = Lang::npc('resistances').Lang::main('colon').'…';
-
- $modes['resistance'][] = '[td]'.$m.' [/td]'.$tmpRes;
- }
-
- // Melee Damage
- $melee = $_altNPCs->getBaseStats('melee');
- if ($_ = $_altNPCs->getField('dmgSchool')) // magic damage
- $modes['melee'][] = sprintf($modeRow, $m, Lang::nf($melee[0]).' - '.Lang::nf($melee[1]).' ('.Lang::game('sc', $_).')');
- else // phys. damage
- $modes['melee'][] = sprintf($modeRow, $m, Lang::nf($melee[0]).' - '.Lang::nf($melee[1]));
-
- // Ranged Damage
- $ranged = $_altNPCs->getBaseStats('ranged');
- $modes['ranged'][] = sprintf($modeRow, $m, Lang::nf($ranged[0]).' - '.Lang::nf($ranged[1]));
- }
- }
- }
-
- if ($modes)
- foreach ($stats as $k => $v)
- if ($v)
- $stats[$k] = sprintf($hint, implode('[/tr][tr]', $modes[$k]), $v, $k);
-
- // < Stats
- if ($stats)
- $infobox[] = Lang::npc('stats').($modes ? ' ('.Lang::npc('modes', $mapType, 0).')' : null).Lang::main('colon').'[ul][li]'.implode('[/li][li]', $stats).'[/li][/ul]';
-
-
- /****************/
- /* Main Content */
- /****************/
-
- // get spawns and path
- $map = null;
- if ($spawns = $this->subject->getSpawns(SPAWNINFO_FULL))
- {
- $map = ['data' => ['parent' => 'mapper-generic'], 'mapperData' => &$spawns];
- foreach ($spawns as $areaId => &$areaData)
- $map['extra'][$areaId] = ZoneList::getName($areaId);
- }
-
- // smart AI
- $sai = null;
- if ($this->subject->getField('aiName') == 'SmartAI')
- {
- $sai = new SmartAI(SAI_SRC_TYPE_CREATURE, $this->typeId, ['name' => $this->subject->getField('name', true)]);
- if (!$sai->prepare()) // no smartAI found .. check per guid
- {
- // at least one of many
- $guids = DB::World()->selectCol('SELECT guid FROM creature WHERE id = ?d', $this->typeId);
- while ($_ = array_pop($guids))
- {
- $sai = new SmartAI(SAI_SRC_TYPE_CREATURE, -$_, ['baseEntry' => $this->typeId, 'name' => $this->subject->getField('name', true), 'title' => ' [small](for GUID: '.$_.')[/small]']);
- if ($sai->prepare())
- break;
- }
- }
-
- if ($sai->prepare())
- $this->extendGlobalData($sai->getJSGlobals());
- else
- trigger_error('Creature has SmartAI set in template but no SmartAI defined.');
- }
-
- // consider pooled spawns
- $this->map = $map;
- $this->infobox = '[ul][li]'.implode('[/li][li]', $infobox).'[/li][/ul]';
- $this->placeholder = $placeholder;
- $this->accessory = $accessory;
- $this->quotes = $this->getQuotes();
- $this->reputation = $this->getOnKillRep($_altIds, $mapType);
- $this->smartAI = $sai ? $sai->getMarkdown() : null;
- $this->redButtons = array(
- BUTTON_WOWHEAD => true,
- BUTTON_LINKS => ['type' => $this->type, 'typeId' => $this->typeId],
- BUTTON_VIEW3D => ['type' => Type::NPC, 'typeId' => $this->typeId, 'displayId' => $this->subject->getRandomModelId()]
- );
-
- if ($this->subject->getField('humanoid'))
- $this->redButtons[BUTTON_VIEW3D]['humanoid'] = 1;
-
-
- /**************/
- /* Extra Tabs */
- /**************/
-
- // tab: abilities / tab_controlledabilities (dep: VehicleId)
- $tplSpells = [];
- $smartSpells = [];
- $conditions = ['OR'];
-
- for ($i = 1; $i < 9; $i++)
- if ($_ = $this->subject->getField('spell'.$i))
- $tplSpells[] = $_;
-
- if ($tplSpells)
- $conditions[] = ['id', $tplSpells];
-
- if ($smartSpells = SmartAI::getSpellCastsForOwner($this->typeId, SAI_SRC_TYPE_CREATURE))
- $conditions[] = ['id', $smartSpells];
-
- // Pet-Abilities
- if ($_typeFlags & 0x1 && ($_ = $this->subject->getField('family')))
- {
- $skill = 0;
- $mask = 0x0;
- foreach (Game::$skillLineMask[-1] as $idx => $pair)
- {
- if ($pair[0] != $_)
- continue;
-
- $skill = $pair[1];
- $mask = 1 << $idx;
- break;
- }
- $conditions[] = [
- 'AND',
- ['s.typeCat', -3],
- [
- 'OR',
- ['skillLine1', $skill],
- ['AND', ['skillLine1', 0, '>'], ['skillLine2OrMask', $skill]],
- ['AND', ['skillLine1', -1], ['skillLine2OrMask', $mask, '&']]
- ]
- ];
- }
-
- if (count($conditions) > 1)
- {
- $abilities = new SpellList($conditions);
- if (!$abilities->error)
- {
- $this->extendGlobalData($abilities->getJSGlobals(GLOBALINFO_SELF | GLOBALINFO_RELATED));
- $controled = $abilities->getListviewData();
- $normal = [];
-
- foreach ($controled as $id => $values)
- {
- if (in_array($id, $smartSpells))
- {
- $normal[$id] = $values;
- if (!in_array($id, $tplSpells))
- unset($controled[$id]);
- }
- }
-
- if ($normal)
- $this->lvTabs[] = ['spell', array(
- 'data' => array_values($normal),
- 'name' => '$LANG.tab_abilities',
- 'id' => 'abilities'
- )];
-
- if ($controled)
- $this->lvTabs[] = ['spell', array(
- 'data' => array_values($controled),
- 'name' => '$LANG.tab_controlledabilities',
- 'id' => 'controlled-abilities'
- )];
- }
- }
-
- // tab: summoned by [spell]
- $conditions = array(
- 'OR',
- ['AND', ['effect1Id', [28, 56, 112]], ['effect1MiscValue', $this->typeId]],
- ['AND', ['effect2Id', [28, 56, 112]], ['effect2MiscValue', $this->typeId]],
- ['AND', ['effect3Id', [28, 56, 112]], ['effect3MiscValue', $this->typeId]]
- );
-
- $sbSpell = new SpellList($conditions);
- if (!$sbSpell->error)
- {
- $this->extendGlobalData($sbSpell->getJSGlobals());
-
- $this->lvTabs[] = ['spell', array(
- 'data' => array_values($sbSpell->getListviewData()),
- 'name' => '$LANG.tab_summonedby',
- 'id' => 'summoned-by-spell'
- )];
- }
-
- // tab: summoned by [NPC]
- $sb = SmartAI::getOwnerOfNPCSummon($this->typeId);
- if (!empty($sb[Type::NPC]))
- {
- $sbNPC = new CreatureList(array(['id', $sb[Type::NPC]]));
- if (!$sbNPC->error)
- {
- $this->extendGlobalData($sbNPC->getJSGlobals());
-
- $this->lvTabs[] = ['creature', array(
- 'data' => array_values($sbNPC->getListviewData()),
- 'name' => '$LANG.tab_summonedby',
- 'id' => 'summoned-by-npc'
- )];
- }
- }
-
- // tab: summoned by [Object]
- if (!empty($sb[Type::OBJECT]))
- {
- $sbGO = new GameObjectList(array(['id', $sb[Type::OBJECT]]));
- if (!$sbGO->error)
- {
- $this->extendGlobalData($sbGO->getJSGlobals());
-
- $this->lvTabs[] = ['object', array(
- 'data' => array_values($sbGO->getListviewData()),
- 'name' => '$LANG.tab_summonedby',
- 'id' => 'summoned-by-object'
- )];
- }
- }
-
- // tab: teaches
- if ($this->subject->getField('npcflag') & NPC_FLAG_TRAINER)
- {
- $teachQuery = '
- SELECT ts.SpellId AS ARRAY_KEY, ts.MoneyCost AS cost, ts.ReqSkillLine AS reqSkillId, ts.ReqSkillRank AS reqSkillValue, ts.ReqLevel AS reqLevel, ts.ReqAbility1 AS reqSpellId1, ts.reqAbility2 AS reqSpellId2
- FROM trainer_spell ts
- JOIN creature_default_trainer cdt ON cdt.TrainerId = ts.TrainerId
- WHERE cdt.Creatureid = ?d
- ';
-
- if ($tSpells = DB::World()->select($teachQuery, $this->typeId))
- {
- $teaches = new SpellList(array(['id', array_keys($tSpells)]));
- if (!$teaches->error)
- {
- $this->extendGlobalData($teaches->getJSGlobals(GLOBALINFO_SELF | GLOBALINFO_RELATED));
- $data = $teaches->getListviewData();
-
- $extra = [];
- foreach ($tSpells as $sId => $train)
- {
- if (empty($data[$sId]))
- continue;
-
- if ($_ = $train['reqSkillId'])
- {
- if (count($data[$sId]['skill']) == 1 && $_ != $data[$sId]['skill'][0])
- {
- $this->extendGlobalIds(Type::SKILL, $_);
- if (!isset($extra[0]))
- $extra[0] = '$Listview.extraCols.condition';
-
- $data[$sId]['condition'][0][$this->typeId][] = [[CND_SKILL, $_, $train['reqSkillValue']]];
- }
- }
-
- for ($i = 1; $i < 3; $i++)
- {
- if ($_ = $train['reqSpellId'.$i])
- {
- $this->extendGlobalIds(Type::SPELL, $_);
- if (!isset($extra[0]))
- $extra[0] = '$Listview.extraCols.condition';
-
- $data[$sId]['condition'][0][$this->typeId][] = [[CND_SPELL, $_]];
- }
- }
-
- if ($_ = $train['reqLevel'])
- {
- if (!isset($extra[1]))
- $extra[1] = "\$Listview.funcBox.createSimpleCol('reqLevel', LANG.tooltip_reqlevel, '7%', 'reqLevel')";
-
- $data[$sId]['reqLevel'] = $_;
- }
-
- if ($_ = $train['cost'])
- $data[$sId]['trainingcost'] = $_;
- }
-
- $tabData = array(
- 'data' => array_values($data),
- 'name' => '$LANG.tab_teaches',
- 'id' => 'teaches',
- 'visibleCols' => ['trainingcost']
- );
-
- if ($extra)
- $tabData['extraCols'] = array_values($extra);
-
- $this->lvTabs[] = ['spell', $tabData];
- }
- }
- else
- trigger_error('NPC '.$this->typeId.' is flagged as trainer, but doesn\'t have any spells set', E_USER_WARNING);
- }
-
- // tab: sells
- if ($sells = DB::World()->selectCol('SELECT item FROM npc_vendor nv WHERE entry = ?d UNION SELECT item FROM game_event_npc_vendor genv JOIN creature c ON genv.guid = c.guid WHERE c.id = ?d', $this->typeId, $this->typeId))
- {
- $soldItems = new ItemList(array(['id', $sells]));
- if (!$soldItems->error)
- {
- $extraCols = ["\$Listview.funcBox.createSimpleCol('stack', 'stack', '10%', 'stack')", '$Listview.extraCols.cost'];
- if ($soldItems->hasSetFields(['condition']))
- $extraCols[] = '$Listview.extraCols.condition';
-
- $lvData = $soldItems->getListviewData(ITEMINFO_VENDOR, [Type::NPC => [$this->typeId]]);
-
- $sc = Util::getServerConditions(CND_SRC_NPC_VENDOR, $this->typeId);
- if (!empty($sc[0]))
- {
- $this->extendGlobalData($sc[1]);
-
- $extraCols[] = '$Listview.extraCols.condition';
-
- foreach ($lvData as $id => &$row)
- foreach ($sc[0] as $srcType => $cndData)
- if (!empty($cndData[$id.':'.$this->typeId]))
- $row['condition'][0][$id.':'.$this->typeId] = $cndData[$id.':'.$this->typeId];
- }
-
- $this->lvTabs[] = ['item', array(
- 'data' => array_values($lvData),
- 'name' => '$LANG.tab_sells',
- 'id' => 'currency-for',
- 'extraCols' => array_unique($extraCols)
- )];
-
- $this->extendGlobalData($soldItems->getJSGlobals(GLOBALINFO_SELF | GLOBALINFO_RELATED));
- }
- }
-
- // tabs: this creature contains..
- $skinTab = ['tab_skinning', 'skinning', SKILL_SKINNING];
- if ($_typeFlags & NPC_TYPEFLAG_HERBLOOT)
- $skinTab = ['tab_herbalism', 'herbalism', SKILL_HERBALISM];
- else if ($_typeFlags & NPC_TYPEFLAG_MININGLOOT)
- $skinTab = ['tab_mining', 'mining', SKILL_MINING];
- else if ($_typeFlags & NPC_TYPEFLAG_ENGINEERLOOT)
- $skinTab = ['tab_engineering', 'engineering', SKILL_ENGINEERING];
-
- /*
- extraCols: [Listview.extraCols.count, Listview.extraCols.percent, Listview.extraCols.mode],
- _totalCount: 22531,
- computeDataFunc: Listview.funcBox.initLootTable,
- onAfterCreate: Listview.funcBox.addModeIndicator,
-
- modes:{"mode":1,"1":{"count":4408,"outof":16013},"4":{"count":4408,"outof":22531}}
- */
-
- $sourceFor = array(
- [LOOT_CREATURE, $this->subject->getField('lootId'), '$LANG.tab_drops', 'drops', [] ],
- [LOOT_PICKPOCKET, $this->subject->getField('pickpocketLootId'), '$LANG.tab_pickpocketing', 'pickpocketing', ['side', 'slot', 'reqlevel']],
- [LOOT_SKINNING, $this->subject->getField('skinLootId'), '$LANG.'.$skinTab[0], $skinTab[1], ['side', 'slot', 'reqlevel']]
- );
-
- // temp: manually add loot for difficulty-versions
- $langref = array(
- "-2" => '$LANG.tab_heroic',
- "-1" => '$LANG.tab_normal',
- 1 => '$$WH.sprintf(LANG.tab_normalX, 10)',
- 2 => '$$WH.sprintf(LANG.tab_normalX, 25)',
- 3 => '$$WH.sprintf(LANG.tab_heroicX, 10)',
- 4 => '$$WH.sprintf(LANG.tab_heroicX, 25)'
- );
-
- if ($_altIds)
- {
- $sourceFor[0][2] = $mapType == 1 ? $langref[-1] : $langref[1];
- foreach ($_altNPCs->iterate() as $id => $__)
- {
- $mode = ($_altIds[$id] + 1) * ($mapType == 1 ? -1 : 1);
- if ($lootGO = DB::Aowow()->selectRow('SELECT o.id, o.lootId, o.name_loc0, o.name_loc2, o.name_loc3, o.name_loc6, o.name_loc8 FROM ?_loot_link l JOIN ?_objects o ON o.id = l.objectId WHERE l.npcId = ?d', $id))
- array_splice($sourceFor, 1, 0, [[LOOT_GAMEOBJECT, $lootGO['lootId'], $langref[$mode], 'drops-object-'.abs($mode), [], 'note' => '$$WH.sprintf(LANG.lvnote_npcobjectsource, '.$lootGO['id'].', "'.Util::localizedString($lootGO, 'name').'")']]);
- if ($lootId = $_altNPCs->getField('lootId'))
- array_splice($sourceFor, 1, 0, [[LOOT_CREATURE, $lootId, $langref[$mode], 'drops-'.abs($mode), []]]);
- }
- }
-
- if ($lootGOs = DB::Aowow()->select('SELECT o.id, IF(npcId < 0, 1, 0) AS modeDummy, o.lootId, o.name_loc0, o.name_loc2, o.name_loc3, o.name_loc6, o.name_loc8 FROM ?_loot_link l JOIN ?_objects o ON o.id = l.objectId WHERE ABS(l.npcId) = ?d', $this->typeId))
- foreach ($lootGOs as $idx => $lgo)
- array_splice($sourceFor, 1, 0, [[LOOT_GAMEOBJECT, $lgo['lootId'], $mapType ? $langref[($mapType == 1 ? -1 : 1) + ($lgo['modeDummy'] ? 1 : 0)] : '$LANG.tab_drops', 'drops-object-'.$idx, [], 'note' => '$$WH.sprintf(LANG.lvnote_npcobjectsource, '.$lgo['id'].', "'.Util::localizedString($lgo, 'name').'")']]);
-
- $lootGOs = DB::World()->select('select SourceEntry, ConditionValue1, ConditionValue2 from conditions where SourceTypeOrReferenceId = 1 and SourceGroup = ?d and ConditionTypeOrReference = ?d', $this->typeId, CND_SKILL);
-
- $reqQuest = [];
- foreach ($sourceFor as $sf)
- {
- $creatureLoot = new Loot();
- if ($creatureLoot->getByContainer($sf[0], $sf[1]))
- {
- $extraCols = $creatureLoot->extraCols;
- $extraCols[] = '$Listview.extraCols.percent';
-
- $this->extendGlobalData($creatureLoot->jsGlobals);
-
- $this->extendWithConditions($creatureLoot, $lootGOs, $extraCols, $reqQuest);
-
- $tabData = array(
- 'data' => array_values($creatureLoot->getResult()),
- 'name' => $sf[2],
- 'id' => $sf[3],
- 'extraCols' => $extraCols,
- 'sort' => ['-percent', 'name'],
- );
-
- if (!empty($sf['note']))
- $tabData['note'] = $sf['note'];
- else if ($sf[0] == LOOT_SKINNING)
- $tabData['note'] = '
'.Lang::formatSkillBreakpoints(Game::getBreakpointsForSkill($skinTab[2], $this->subject->getField('maxLevel') * 5), true).'';
-
- if ($sf[4])
- $tabData['hiddenCols'] = $sf[4];
-
- $this->lvTabs[] = ['item', $tabData];
- }
- }
-
- if ($reqIds = array_keys($reqQuest)) // apply quest-conditions as back-reference
- {
- $conditions = array(
- 'OR',
- ['reqSourceItemId1', $reqIds], ['reqSourceItemId2', $reqIds],
- ['reqSourceItemId3', $reqIds], ['reqSourceItemId4', $reqIds],
- ['reqItemId1', $reqIds], ['reqItemId2', $reqIds], ['reqItemId3', $reqIds],
- ['reqItemId4', $reqIds], ['reqItemId5', $reqIds], ['reqItemId6', $reqIds]
- );
-
- $reqQuests = new QuestList($conditions);
- $this->extendGlobalData($reqQuests->getJSGlobals());
-
- foreach ($reqQuests->iterate() as $qId => $__)
- {
- if (empty($reqQuests->requires[$qId][Type::ITEM]))
- continue;
-
- foreach ($reqIds as $rId)
- if (in_array($rId, $reqQuests->requires[$qId][Type::ITEM]))
- $reqQuest[$rId] = $reqQuests->id;
- }
- }
-
- // tab: starts quest
- // tab: ends quest
- $startEnd = new QuestList(array(['qse.type', Type::NPC], ['qse.typeId', $this->typeId]));
- if (!$startEnd->error)
- {
- $this->extendGlobalData($startEnd->getJSGlobals());
- $lvData = $startEnd->getListviewData();
- $_ = [[], []];
-
- foreach ($startEnd->iterate() as $id => $__)
- {
- $m = $startEnd->getField('method');
- if ($m & 0x1)
- $_[0][] = $lvData[$id];
- if ($m & 0x2)
- $_[1][] = $lvData[$id];
- }
-
- if ($_[0])
- $this->lvTabs[] = ['quest', array(
- 'data' => array_values($_[0]),
- 'name' => '$LANG.tab_starts',
- 'id' => 'starts'
- )];
-
- if ($_[1])
- $this->lvTabs[] = ['quest', array(
- 'data' => array_values($_[1]),
- 'name' => '$LANG.tab_ends',
- 'id' => 'ends'
- )];
- }
-
- // tab: objective of quest
- $conditions = array(
- 'OR',
- ['AND', ['reqNpcOrGo1', $this->typeId], ['reqNpcOrGoCount1', 0, '>']],
- ['AND', ['reqNpcOrGo2', $this->typeId], ['reqNpcOrGoCount2', 0, '>']],
- ['AND', ['reqNpcOrGo3', $this->typeId], ['reqNpcOrGoCount3', 0, '>']],
- ['AND', ['reqNpcOrGo4', $this->typeId], ['reqNpcOrGoCount4', 0, '>']],
- );
-
- $objectiveOf = new QuestList($conditions);
- if (!$objectiveOf->error)
- {
- $this->extendGlobalData($objectiveOf->getJSGlobals());
-
- $this->lvTabs[] = ['quest', array(
- 'data' => array_values($objectiveOf->getListviewData()),
- 'name' => '$LANG.tab_objectiveof',
- 'id' => 'objective-of'
- )];
- }
-
- // tab: criteria of [ACHIEVEMENT_CRITERIA_TYPE_KILL_CREATURE_TYPE have no data set to check for]
- $conditions = array(
- ['ac.type', [ACHIEVEMENT_CRITERIA_TYPE_KILL_CREATURE, ACHIEVEMENT_CRITERIA_TYPE_KILLED_BY_CREATURE]],
- ['ac.value1', $this->typeId]
- );
-
- $crtOf = new AchievementList($conditions);
- if (!$crtOf->error)
- {
- $this->extendGlobalData($crtOf->getJSGlobals());
-
- $this->lvTabs[] = ['achievement', array(
- 'data' => array_values($crtOf->getListviewData()),
- 'name' => '$LANG.tab_criteriaof',
- 'id' => 'criteria-of'
- )];
- }
-
- // tab: passengers
- if ($_ = DB::World()->selectCol('SELECT accessory_entry AS ARRAY_KEY, GROUP_CONCAT(seat_id) FROM vehicle_template_accessory WHERE entry = ?d GROUP BY accessory_entry', $this->typeId))
- {
- $passengers = new CreatureList(array(['id', array_keys($_)]));
- if (!$passengers->error)
- {
- $data = $passengers->getListviewData();
-
- if (User::isInGroup(U_GROUP_STAFF))
- foreach ($data as $id => &$d)
- $d['seat'] = str_replace(',', ', ', $_[$id]);
-
- $this->extendGlobalData($passengers->getJSGlobals(GLOBALINFO_SELF));
-
- $tabData = array(
- 'data' => array_values($data),
- 'name' => Lang::npc('accessory'),
- 'id' => 'accessory'
- );
-
- if (User::isInGroup(U_GROUP_STAFF))
- $tabData['extraCols'] = ["\$Listview.funcBox.createSimpleCol('seat', '".Lang::npc('seat')."', '10%', 'seat')"];
-
- $this->lvTabs[] = ['creature', $tabData];
- }
- }
-
- /* tab sounds:
- * activity sounds => CreatureDisplayInfo.dbc => (CreatureModelData.dbc => ) CreatureSoundData.dbc
- * AI => smart_scripts
- * Dialogue VO => creature_text
- * onClick VO => CreatureDisplayInfo.dbc => NPCSounds.dbc
- */
- $this->soundIds = array_merge($this->soundIds, SmartAI::getSoundsPlayedForOwner($this->typeId, SAI_SRC_TYPE_CREATURE));
-
- // up to 4 possible displayIds .. for the love of things betwixt, just use the first!
- $activitySounds = DB::Aowow()->selectRow('SELECT * FROM ?_creature_sounds WHERE id = ?d', $this->subject->getField('displayId1'));
- array_shift($activitySounds); // remove id-column
- $this->soundIds = array_merge($this->soundIds, array_values($activitySounds));
-
- if ($this->soundIds)
- {
- $sounds = new SoundList(array(['id', $this->soundIds]));
- if (!$sounds->error)
- {
- $data = $sounds->getListviewData();
- foreach ($activitySounds as $activity => $id)
- if (isset($data[$id]))
- $data[$id]['activity'] = $activity; // no index, js wants a string :(
-
- $tabData = ['data' => array_values($data)];
- if ($activitySounds)
- $tabData['visibleCols'] = ['activity'];
-
- $this->extendGlobalData($sounds->getJSGlobals(GLOBALINFO_SELF));
- $this->lvTabs[] = ['sound', $tabData];
- }
- }
- }
-
- protected function generateTooltip()
- {
- $power = new StdClass();
- if (!$this->subject->error)
- {
- $power->{'name_'.User::$localeString} = $this->subject->getField('name', true);
- $power->{'tooltip_'.User::$localeString} = $this->subject->renderTooltip();
- $power->map = $this->subject->getSpawns(SPAWNINFO_SHORT);
- }
-
- return sprintf($this->powerTpl, $this->typeId, User::$localeId, Util::toJSON($power, JSON_AOWOW_POWER));
- }
-
- private function getRepForId($entries, &$spillover)
- {
- $rows = DB::World()->select('
- SELECT creature_id AS npc, RewOnKillRepFaction1 AS faction, RewOnKillRepValue1 AS qty, MaxStanding1 AS maxRank, isTeamAward1 AS spillover
- FROM creature_onkill_reputation WHERE creature_id IN (?a) AND RewOnKillRepFaction1 > 0 UNION
- SELECT creature_id AS npc, RewOnKillRepFaction2 As faction, RewOnKillRepValue2 AS qty, MaxStanding2 AS maxRank, isTeamAward2 AS spillover
- FROM creature_onkill_reputation WHERE creature_id IN (?a) AND RewOnKillRepFaction2 > 0',
- (array)$entries, (array)$entries
- );
-
- $factions = new FactionList(array(['id', array_column($rows, 'faction')]));
- $result = [];
-
- foreach ($rows as $row)
- {
- if (!$factions->getEntry($row['faction']))
- continue;
-
- $set = array(
- 'id' => $row['faction'],
- 'qty' => [$row['qty'], 0],
- 'name' => $factions->getField('name', true),
- 'npc' => $row['npc'],
- 'cap' => $row['maxRank'] && $row['maxRank'] < REP_EXALTED ? Lang::game('rep', $row['maxRank']) : null
- );
-
- $cuRate = DB::World()->selectCell('SELECT creature_rate FROM reputation_reward_rate WHERE creature_rate <> 1 AND faction = ?d', $row['faction']);
- if ($cuRate !== null)
- $set['qty'][1] = $set['qty'][0] * ($cuRate - 1);
-
- if ($row['spillover'])
- {
- $spillover[$factions->getField('cat')] = array(
- [ $set['qty'][0] / 2, $set['qty'][1] / 2 ],
- $row['maxRank']
- );
- $set['spillover'] = $factions->getField('cat');
- }
-
- $result[] = $set;
- }
-
- return $result;
- }
-
- private function getOnKillRep($dummyIds, $mapType)
- {
- $spilledParents = [];
- $reputation = [];
-
- // base NPC
- if ($base = $this->getRepForId($this->typeId, $spilledParents))
- $reputation[] = [Lang::npc('modes', 1, 0), $base];
-
- // difficulty dummys
- if ($dummyIds && ($mapType == 1 || $mapType == 2))
- {
- $alt = [];
- $rep = $this->getRepForId(array_keys($dummyIds), $spilledParents);
-
- // order by difficulty
- foreach ($rep as $r)
- $alt[$dummyIds[$r['npc']]][] = $r;
-
- // apply by difficulty
- foreach ($alt as $mode => $dat)
- $reputation[] = [Lang::npc('modes', $mapType, $mode), $dat];
- }
-
- // get spillover factions and apply
- if ($spilledParents)
- {
- $spilled = new FactionList(array(['parentFactionId', array_keys($spilledParents)]));
-
- foreach ($reputation as &$sets)
- {
- foreach ($sets[1] as &$row)
- {
- if (empty($row['spillover']))
- continue;
-
- foreach ($spilled->iterate() as $spId => $__)
- {
- // find parent
- if ($spilled->getField('parentFactionId') != $row['spillover'])
- continue;
-
- // don't readd parent
- if ($row['id'] == $spId)
- continue;
-
- $spMax = $spilledParents[$row['spillover']][1];
-
- $sets[1][] = array(
- 'id' => $spId,
- 'qty' => $spilledParents[$row['spillover']][0],
- 'name' => $spilled->getField('name', true),
- 'cap' => $spMax && $spMax < REP_EXALTED ? Lang::game('rep', $spMax) : null
- );
- }
- }
- }
- }
-
- return $reputation;
- }
-
- private function getQuotes()
- {
- [$quotes, $nQuotes, $soundIds] = Game::getQuotesForCreature($this->typeId, true, $this->subject->getField('name', true));
-
- if ($soundIds)
- $this->soundIds = array_merge($this->soundIds, $soundIds);
-
- return [$quotes, $nQuotes];
- }
-
- private function getConditions($itemId, $data)
- {
- foreach ($data as $datum)
- {
- if ($datum['SourceEntry'] == $itemId)
- {
- return [CND_SKILL, $datum['ConditionValue1'], $datum['ConditionValue2']];
- }
- }
-
- return null;
- }
-
- private function extendWithConditions($creatureLoot, $lootConditions, &$extraCols, &$reqQuest)
- {
- $hasExtraCol = false;
- $reqSkill = [];
-
- foreach ($creatureLoot->iterate() as &$lv)
- {
- if ($lv['quest'])
- {
- $hasExtraCol = true;
- $reqQuest[$lv['id']] = 0;
- $lv['condition'][0][$this->typeId][] = [[CND_QUESTTAKEN, &$reqQuest[$lv['id']]]];
- }
- elseif ($skill = $this->getConditions($lv['id'], $lootConditions))
- {
- $hasExtraCol = true;
- $lv['condition'][0][$this->typeId][] = [$skill];
- $reqSkill[] = $skill[1];
- }
- }
-
- if ($hasExtraCol)
- {
- $extraCols[] = '$Listview.extraCols.condition';
- }
-
- $reqSkills = new SkillList(['OR', ['id', array_unique($reqSkill)]]);
- $this->extendGlobalData($reqSkills->getJSGlobals());
- }
-}
-
-
-?>
diff --git a/pages/npcs.php b/pages/npcs.php
deleted file mode 100644
index 96175e78..00000000
--- a/pages/npcs.php
+++ /dev/null
@@ -1,121 +0,0 @@
- ['filter' => FILTER_UNSAFE_RAW]];
-
- public function __construct($pageCall, $pageParam)
- {
- $this->getCategoryFromUrl($pageParam);;
- $this->filterObj = new CreatureListFilter(false, ['parentCats' => $this->category]);
-
- parent::__construct($pageCall, $pageParam);
-
- $this->name = Util::ucFirst(Lang::game('npcs'));
- $this->subCat = $pageParam ? '='.$pageParam : '';
- }
-
- protected function generateContent()
- {
- $this->addScript([JS_FILE, '?data=zones&locale='.User::$localeId.'&t='.$_SESSION['dataKey']]);
-
- $conditions = [];
-
- if (!User::isInGroup(U_GROUP_EMPLOYEE))
- $conditions[] = [['cuFlags', CUSTOM_EXCLUDE_FOR_LISTVIEW, '&'], 0];
-
- if ($this->category)
- {
- $conditions[] = ['type', $this->category[0]];
- $this->petFamPanel = $this->category[0] == 1;
- }
- else
- $this->petFamPanel = false;
-
- if ($_ = $this->filterObj->getConditions())
- $conditions[] = $_;
-
- // beast subtypes are selected via filter
- $npcs = new CreatureList($conditions, ['extraOpts' => $this->filterObj->extraOpts]);
-
- // recreate form selection
- $this->filter = $this->filterObj->getForm();
- $this->filter['query'] = $this->_get['filter'];
- $this->filter['initData'] = ['init' => 'npcs'];
-
- $rCols = $this->filterObj->getReputationCols();
- $xCols = $this->filterObj->getExtraCols();
- if ($rCols)
- $this->filter['initData']['rc'] = $rCols;
-
- if ($xCols)
- $this->filter['initData']['ec'] = $xCols;
-
- if ($x = $this->filterObj->getSetCriteria())
- $this->filter['initData']['sc'] = $x;
-
- $tabData = ['data' => array_values($npcs->getListviewData($rCols ? NPCINFO_REP : 0x0))];
-
- if ($rCols) // never use pretty-print
- $tabData['extraCols'] = '$fi_getReputationCols('.Util::toJSON($rCols, JSON_NUMERIC_CHECK | JSON_UNESCAPED_UNICODE).')';
- else if ($xCols)
- $tabData['extraCols'] = '$fi_getExtraCols(fi_extraCols, 0, 0)';
-
- if ($this->category)
- $tabData['hiddenCols'] = ['type'];
-
- // create note if search limit was exceeded
- if ($npcs->getMatches() > CFG_SQL_LIMIT_DEFAULT)
- {
- $tabData['note'] = sprintf(Util::$tryFilteringString, 'LANG.lvnote_npcsfound', $npcs->getMatches(), CFG_SQL_LIMIT_DEFAULT);
- $tabData['_truncated'] = 1;
- }
-
- if ($this->filterObj->error)
- $tabData['_errors'] = 1;
-
- $this->lvTabs[] = ['creature', $tabData];
-
- // sort for dropdown-menus
- Lang::sort('game', 'fa');
- }
-
- protected function generateTitle()
- {
- array_unshift($this->title, $this->name);
- if ($this->category)
- array_unshift($this->title, Lang::npc('cat', $this->category[0]));
-
- $form = $this->filterObj->getForm();
- if (isset($form['fa']) && !is_array($form['fa']))
- array_unshift($this->title, Lang::game('fa', $form['fa']));
- }
-
- protected function generatePath()
- {
- if ($this->category)
- $this->path[] = $this->category[0];
-
- $form = $this->filterObj->getForm();
- if (isset($form['fa']) && !is_array($form['fa']))
- $this->path[] = $form['fa'];
- }
-}
-
-?>
diff --git a/pages/object.php b/pages/object.php
deleted file mode 100644
index 9e31ddc6..00000000
--- a/pages/object.php
+++ /dev/null
@@ -1,513 +0,0 @@
- ['filter' => FILTER_CALLBACK, 'options' => 'GenericPage::checkDomain']];
-
- private $powerTpl = '$WowheadPower.registerObject(%d, %d, %s);';
-
- public function __construct($pageCall, $id)
- {
- parent::__construct($pageCall, $id);
-
- // temp locale
- if ($this->mode == CACHE_TYPE_TOOLTIP && $this->_get['domain'])
- Util::powerUseLocale($this->_get['domain']);
-
- $this->typeId = intVal($id);
-
- $this->subject = new GameObjectList(array(['id', $this->typeId]));
- if ($this->subject->error)
- $this->notFound(Lang::game('object'), Lang::gameObject('notFound'));
-
- $this->name = $this->subject->getField('name', true);
- }
-
- protected function generatePath()
- {
- $this->path[] = $this->subject->getField('typeCat');
- }
-
- protected function generateTitle()
- {
- array_unshift($this->title, $this->name, Util::ucFirst(Lang::game('object')));
- }
-
- protected function generateContent()
- {
- $this->addScript([JS_FILE, '?data=zones&locale='.User::$localeId.'&t='.$_SESSION['dataKey']]);
-
- /***********/
- /* Infobox */
- /***********/
-
- $infobox = Lang::getInfoBoxForFlags($this->subject->getField('cuFlags'));
-
- // Event (ignore events, where the object only gets removed)
- if ($_ = DB::World()->selectCol('SELECT DISTINCT ge.eventEntry FROM game_event ge, game_event_gameobject geg, gameobject g WHERE ge.eventEntry = geg.eventEntry AND g.guid = geg.guid AND g.id = ?d', $this->typeId))
- {
- $this->extendGlobalIds(Type::WORLDEVENT, ...$_);
- $ev = [];
- foreach ($_ as $i => $e)
- $ev[] = ($i % 2 ? '[br]' : ' ') . '[event='.$e.']';
-
- $infobox[] = Util::ucFirst(Lang::game('eventShort')).Lang::main('colon').implode(',', $ev);
- }
-
- // Faction
- if ($_ = DB::Aowow()->selectCell('SELECT factionId FROM ?_factiontemplate WHERE id = ?d', $this->subject->getField('faction')))
- {
- $this->extendGlobalIds(Type::FACTION, $_);
- $infobox[] = Util::ucFirst(Lang::game('faction')).Lang::main('colon').'[faction='.$_.']';
- }
-
- // Reaction
- $_ = function ($r)
- {
- if ($r == 1) return 2; // q2 green
- if ($r == -1) return 10; // q10 red
- return; // q yellow
- };
- $infobox[] = Lang::npc('react').Lang::main('colon').'[color=q'.$_($this->subject->getField('A')).']A[/color] [color=q'.$_($this->subject->getField('H')).']H[/color]';
-
- // reqSkill + difficulty
- switch ($this->subject->getField('typeCat'))
- {
- case -3: // Herbalism
- $infobox[] = sprintf(Lang::game('requires'), Lang::spell('lockType', 2).' ('.$this->subject->getField('reqSkill').')');
- $infobox[] = Lang::formatSkillBreakpoints(Game::getBreakpointsForSkill(SKILL_HERBALISM, $this->subject->getField('reqSkill')));
- break;
- case -4: // Mining
- $infobox[] = sprintf(Lang::game('requires'), Lang::spell('lockType', 3).' ('.$this->subject->getField('reqSkill').')');
- $infobox[] = Lang::formatSkillBreakpoints(Game::getBreakpointsForSkill(SKILL_MINING, $this->subject->getField('reqSkill')));
- break;
- case -5: // Lockpicking
- $infobox[] = sprintf(Lang::game('requires'), Lang::spell('lockType', 1).' ('.$this->subject->getField('reqSkill').')');
- $infobox[] = Lang::formatSkillBreakpoints(Game::getBreakpointsForSkill(SKILL_LOCKPICKING, $this->subject->getField('reqSkill')));
- break;
- default: // requires key .. maybe
- {
- $locks = Lang::getLocks($this->subject->getField('lockId'), $ids, true);
- $l = [];
-
- foreach ($ids as $type => $typeIds)
- $this->extendGlobalIds($type, ...$typeIds);
-
- foreach ($locks as $idx => $str)
- {
- if ($idx > 0)
- $l[] = Lang::gameObject('key').Lang::main('colon').$str;
- else if ($idx < 0)
- $l[] = sprintf(Lang::game('requires'), $str);
- }
-
- if ($l)
- $infobox[] = implode('[br]', $l);
- }
- }
-
- // linked trap
- if ($_ = $this->subject->getField('linkedTrap'))
- {
- $this->extendGlobalIds(Type::OBJECT, $_);
- $infobox[] = Lang::gameObject('trap').Lang::main('colon').'[object='.$_.']';
- }
-
- // trap for
- $trigger = new GameObjectList(array(['linkedTrap', $this->typeId]));
- if (!$trigger->error)
- {
- $this->extendGlobalData($trigger->getJSGlobals());
- $infobox[] = Lang::gameObject('triggeredBy').Lang::main('colon').'[object='.$trigger->id.']';
- }
-
- // SpellFocus
- if ($_ = $this->subject->getField('spellFocusId'))
- if ($sfo = DB::Aowow()->selectRow('SELECT * FROM ?_spellfocusobject WHERE id = ?d', $_))
- $infobox[] = '[tooltip name=focus]'.Lang::gameObject('focusDesc').'[/tooltip][span class=tip tooltip=focus]'.Lang::gameObject('focus').Lang::main('colon').Util::localizedString($sfo, 'name').'[/span]';
-
- // lootinfo: [min, max, restock]
- if (($_ = $this->subject->getField('lootStack')) && $_[0])
- {
- $buff = Lang::spell('spellModOp', 4).Lang::main('colon').$_[0];
- if ($_[0] < $_[1])
- $buff .= Lang::game('valueDelim').$_[1];
-
- // since Veins don't have charges anymore, the timer is questionable
- $infobox[] = $_[2] > 1 ? '[tooltip name=restock]'.sprintf(Lang::gameObject('restock'), Util::formatTime($_[2] * 1000)).'[/tooltip][span class=tip tooltip=restock]'.$buff.'[/span]' : $buff;
- }
-
- // meeting stone [minLevel, maxLevel, zone]
- if ($this->subject->getField('type') == OBJECT_MEETINGSTONE)
- {
- if ($_ = $this->subject->getField('mStone'))
- {
- $this->extendGlobalIds(Type::ZONE, $_[2]);
- $m = Lang::game('meetingStone').Lang::main('colon').'[zone='.$_[2].']';
-
- $l = $_[0];
- if ($_[0] > 1 && $_[1] > $_[0])
- $l .= Lang::game('valueDelim').min($_[1], MAX_LEVEL);
-
- $infobox[] = $l ? '[tooltip name=meetingstone]'.sprintf(Lang::game('reqLevel'), $l).'[/tooltip][span class=tip tooltip=meetingstone]'.$m.'[/span]' : $m;
- }
- }
-
- // capture area [minPlayer, maxPlayer, minTime, maxTime, radius]
- if ($this->subject->getField('type') == OBJECT_CAPTURE_POINT)
- {
- if ($_ = $this->subject->getField('capture'))
- {
- $buff = Lang::gameObject('capturePoint');
-
- if ($_[2] > 1 || $_[0])
- $buff .= Lang::main('colon').'[ul]';
-
- if ($_[2] > 1)
- $buff .= '[li]'.Lang::game('duration').Lang::main('colon').($_[3] > $_[2] ? Util::FormatTime($_[3] * 1000, true).' - ' : null).Util::FormatTime($_[2] * 1000, true).'[/li]';
-
- if ($_[1])
- $buff .= '[li]'.Lang::main('players').Lang::main('colon').$_[0].($_[1] > $_[0] ? ' - '.$_[1] : null).'[/li]';
-
- if ($_[4])
- $buff .= '[li]'.sprintf(Lang::spell('range'), $_[4]).'[/li]';
-
- if ($_[2] > 1 || $_[0])
- $buff .= '[/ul]';
- }
-
- $infobox[] = $buff;
- }
-
- // AI
- if (User::isInGroup(U_GROUP_EMPLOYEE))
- {
- if ($_ = $this->subject->getField('ScriptOrAI'))
- {
- if ($_ == 'SmartGameObjectAI')
- $infobox[] = 'AI'.Lang::main('colon').$_;
- else
- $infobox[] = 'Script'.Lang::main('colon').$_;
- }
- }
-
-
- /****************/
- /* Main Content */
- /****************/
-
- // pageText
- if ($this->pageText = Game::getPageText($next = $this->subject->getField('pageTextId')))
- {
- $this->addScript(
- [JS_FILE, 'Book.js'],
- [CSS_FILE, 'Book.css']
- );
- }
-
- // get spawns and path
- $map = null;
- if ($spawns = $this->subject->getSpawns(SPAWNINFO_FULL))
- {
- $map = ['data' => ['parent' => 'mapper-generic'], 'mapperData' => &$spawns];
- foreach ($spawns as $areaId => &$areaData)
- $map['extra'][$areaId] = ZoneList::getName($areaId);
- }
-
-
- // todo (low): consider pooled spawns
-
-
- $relBoss = null;
- if ($ll = DB::Aowow()->selectRow('SELECT * FROM ?_loot_link WHERE objectId = ?d ORDER BY priority DESC LIMIT 1', $this->typeId))
- {
- // group encounter
- if ($ll['encounterId'])
- $relBoss = [$ll['npcId'], Lang::profiler('encounterNames', $ll['encounterId'])];
- // difficulty dummy
- else if ($c = DB::Aowow()->selectRow('SELECT id, name_loc0, name_loc2, name_loc3, name_loc6, name_loc8 FROM ?_creature WHERE difficultyEntry1 = ?d OR difficultyEntry2 = ?d OR difficultyEntry3 = ?d', abs($ll['npcId']), abs($ll['npcId']), abs($ll['npcId'])))
- $relBoss = [$c['id'], Util::localizedString($c, 'name')];
- // base creature
- else if ($c = DB::Aowow()->selectRow('SELECT id, name_loc0, name_loc2, name_loc3, name_loc6, name_loc8 FROM ?_creature WHERE id = ?d', abs($ll['npcId'])))
- $relBoss = [$c['id'], Util::localizedString($c, 'name')];
- }
-
- // smart AI
- $sai = null;
- if ($this->subject->getField('ScriptOrAI') == 'SmartGameObjectAI')
- {
- $sai = new SmartAI(SAI_SRC_TYPE_OBJECT, $this->typeId, ['name' => $this->name]);
- if (!$sai->prepare()) // no smartAI found .. check per guid
- {
- // at least one of many
- $guids = DB::World()->selectCol('SELECT guid FROM gameobject WHERE id = ?d LIMIT 1', $this->typeId);
- while ($_ = array_pop($guids))
- {
- $sai = new SmartAI(SAI_SRC_TYPE_OBJECT, -$_, ['name' => $this->name, 'title' => ' [small](for GUID: '.$_.')[/small]']);
- if ($sai->prepare())
- break;
- }
- }
-
- if ($sai->prepare())
- $this->extendGlobalData($sai->getJSGlobals());
- else
- trigger_error('Gameobject has AIName set in template but no SmartAI defined.');
- }
-
- $this->map = $map;
- $this->infobox = $infobox ? '[ul][li]'.implode('[/li][li]', $infobox).'[/li][/ul]' : null;
- $this->relBoss = $relBoss;
- $this->smartAI = $sai ? $sai->getMarkdown() : null;
- $this->redButtons = array(
- BUTTON_WOWHEAD => true,
- BUTTON_LINKS => ['type' => $this->type, 'typeId' => $this->typeId],
- BUTTON_VIEW3D => ['displayId' => $this->subject->getField('displayId'), 'type' => Type::OBJECT, 'typeId' => $this->typeId]
- );
-
-
- /**************/
- /* Extra Tabs */
- /**************/
-
- // tab: summoned by
- $conditions = array(
- 'OR',
- ['AND', ['effect1Id', [50, 76, 104, 105, 106, 107]], ['effect1MiscValue', $this->typeId]],
- ['AND', ['effect2Id', [50, 76, 104, 105, 106, 107]], ['effect2MiscValue', $this->typeId]],
- ['AND', ['effect3Id', [50, 76, 104, 105, 106, 107]], ['effect3MiscValue', $this->typeId]]
- );
-
- $summons = new SpellList($conditions);
- if (!$summons->error)
- {
- $this->extendGlobalData($summons->getJSGlobals(GLOBALINFO_SELF | GLOBALINFO_RELATED));
-
- $this->lvTabs[] = ['spell', array(
- 'data' => array_values($summons->getListviewData()),
- 'id' => 'summoned-by',
- 'name' => '$LANG.tab_summonedby'
- )];
- }
-
- // tab: related spells
- if ($_ = $this->subject->getField('spells'))
- {
- $relSpells = new SpellList(array(['id', $_]));
- if (!$relSpells->error)
- {
- $this->extendGlobalData($relSpells->getJSGlobals(GLOBALINFO_SELF | GLOBALINFO_RELATED));
- $data = $relSpells->getListviewData();
-
- foreach ($data as $relId => $d)
- $data[$relId]['trigger'] = array_search($relId, $_);
-
- $this->lvTabs[] = ['spell', array(
- 'data' => array_values($data),
- 'id' => 'spells',
- 'name' => '$LANG.tab_spells',
- 'hiddenCols' => ['skill'],
- 'extraCols' => ["\$Listview.funcBox.createSimpleCol('trigger', 'Condition', '10%', 'trigger')"]
- )];
- }
- }
-
- // tab: criteria of
- $acvs = new AchievementList(array(['ac.type', [ACHIEVEMENT_CRITERIA_TYPE_USE_GAMEOBJECT, ACHIEVEMENT_CRITERIA_TYPE_FISH_IN_GAMEOBJECT]], ['ac.value1', $this->typeId]));
- if (!$acvs->error)
- {
- $this->extendGlobalData($acvs->getJSGlobals(GLOBALINFO_SELF | GLOBALINFO_RELATED));
-
- $this->lvTabs[] = ['achievement', array(
- 'data' => array_values($acvs->getListviewData()),
- 'id' => 'criteria-of',
- 'name' => '$LANG.tab_criteriaof'
- )];
- }
-
- // tab: starts quest
- // tab: ends quest
- $startEnd = new QuestList(array(['qse.type', Type::OBJECT], ['qse.typeId', $this->typeId]));
- if (!$startEnd->error)
- {
- $this->extendGlobalData($startEnd->getJSGlobals());
- $lvData = $startEnd->getListviewData();
- $_ = [[], []];
-
- foreach ($startEnd->iterate() as $id => $__)
- {
- $m = $startEnd->getField('method');
- if ($m & 0x1)
- $_[0][] = $lvData[$id];
- if ($m & 0x2)
- $_[1][] = $lvData[$id];
- }
-
- if ($_[0])
- $this->lvTabs[] = ['quest', array(
- 'data' => array_values($_[0]),
- 'name' => '$LANG.tab_starts',
- 'id' => 'starts'
- )];
-
- if ($_[1])
- $this->lvTabs[] = ['quest', array(
- 'data' => array_values($_[1]),
- 'name' => '$LANG.tab_ends',
- 'id' => 'ends'
- )];
- }
-
- // tab: related quests
- if ($_ = $this->subject->getField('reqQuest'))
- {
- $relQuest = new QuestList(array(['id', $_]));
- if (!$relQuest->error)
- {
- $this->extendGlobalData($relQuest->getJSGlobals());
-
- $this->lvTabs[] = ['quest', array(
- 'data' => array_values($relQuest->getListviewData()),
- 'name' => '$LANG.tab_quests',
- 'id' => 'quests'
- )];
- }
- }
-
- // tab: contains
- $reqQuest = [];
- if ($_ = $this->subject->getField('lootId'))
- {
- $goLoot = new Loot();
- if ($goLoot->getByContainer(LOOT_GAMEOBJECT, $_))
- {
- $extraCols = $goLoot->extraCols;
- $extraCols[] = '$Listview.extraCols.percent';
- $hiddenCols = ['source', 'side', 'slot', 'reqlevel'];
-
- $this->extendGlobalData($goLoot->jsGlobals);
-
- foreach ($goLoot->iterate() as &$lv)
- {
- if (!empty($hiddenCols))
- foreach ($hiddenCols as $k => $str)
- if (!empty($lv[$str]))
- unset($hiddenCols[$k]);
-
- if (!$lv['quest'])
- continue;
-
- $extraCols[] = '$Listview.extraCols.condition';
- $reqQuest[$lv['id']] = 0;
- $lv['condition'][0][$this->typeId][] = [[CND_QUESTTAKEN, &$reqQuest[$lv['id']]]];
- }
-
- $tabData = array(
- 'data' => array_values($goLoot->getResult()),
- 'id' => 'contains',
- 'name' => '$LANG.tab_contains',
- 'sort' => ['-percent', 'name'],
- 'extraCols' => array_unique($extraCols)
- );
-
- if ($hiddenCols)
- $tabData['hiddenCols'] = $hiddenCols;
-
- $this->lvTabs[] = ['item', $tabData];
- }
- }
-
- if ($reqIds = array_keys($reqQuest)) // apply quest-conditions as back-reference
- {
- $conditions = array(
- 'OR',
- ['reqSourceItemId1', $reqIds], ['reqSourceItemId2', $reqIds],
- ['reqSourceItemId3', $reqIds], ['reqSourceItemId4', $reqIds],
- ['reqItemId1', $reqIds], ['reqItemId2', $reqIds], ['reqItemId3', $reqIds],
- ['reqItemId4', $reqIds], ['reqItemId5', $reqIds], ['reqItemId6', $reqIds]
- );
-
- $reqQuests = new QuestList($conditions);
- $this->extendGlobalData($reqQuests->getJSGlobals());
-
- foreach ($reqQuests->iterate() as $qId => $__)
- {
- if (empty($reqQuests->requires[$qId][Type::ITEM]))
- continue;
-
- foreach ($reqIds as $rId)
- if (in_array($rId, $reqQuests->requires[$qId][Type::ITEM]))
- $reqQuest[$rId] = $reqQuests->id;
- }
- }
-
- // tab: Spell Focus for
- if ($sfId = $this->subject->getField('spellFocusId'))
- {
- $focusSpells = new SpellList(array(['spellFocusObject', $sfId]));
- if (!$focusSpells->error)
- {
- $tabData = array(
- 'data' => array_values($focusSpells->getListviewData()),
- 'name' => Lang::gameObject('focus'),
- 'id' => 'focus-for'
- );
-
- $this->extendGlobalData($focusSpells->getJSGlobals(GLOBALINFO_SELF | GLOBALINFO_RELATED));
-
- // create note if search limit was exceeded
- if ($focusSpells->getMatches() > CFG_SQL_LIMIT_DEFAULT)
- {
- $tabData['note'] = sprintf(Util::$tryNarrowingString, 'LANG.lvnote_spellsfound', $focusSpells->getMatches(), CFG_SQL_LIMIT_DEFAULT);
- $tabData['_truncated'] = 1;
- }
-
- $this->lvTabs[] = ['spell', $tabData];
- }
- }
-
- // tab: Same model as .. whats the fucking point..?
- $sameModel = new GameObjectList(array(['displayId', $this->subject->getField('displayId')], ['id', $this->typeId, '!']));
- if (!$sameModel->error)
- {
- $this->extendGlobalData($sameModel->getJSGlobals());
-
- $this->lvTabs[] = ['object', array(
- 'data' => array_values($sameModel->getListviewData()),
- 'name' => '$LANG.tab_samemodelas',
- 'id' => 'same-model-as'
- )];
- }
- }
-
- protected function generateTooltip()
- {
- $power = new StdClass();
- if (!$this->subject->error)
- {
- $power->{'name_'.User::$localeString} = $this->subject->getField('name', true);
- $power->{'tooltip_'.User::$localeString} = $this->subject->renderTooltip();
- $power->map = $this->subject->getSpawns(SPAWNINFO_SHORT);
- }
-
- return sprintf($this->powerTpl, $this->typeId, User::$localeId, Util::toJSON($power, JSON_AOWOW_POWER));
- }
-}
-
-?>
diff --git a/pages/objects.php b/pages/objects.php
deleted file mode 100644
index 028c88de..00000000
--- a/pages/objects.php
+++ /dev/null
@@ -1,93 +0,0 @@
- ['filter' => FILTER_UNSAFE_RAW]];
-
- public function __construct($pageCall, $pageParam)
- {
- $this->getCategoryFromUrl($pageParam);;
- $this->filterObj = new GameObjectListFilter(false, ['parentCats' => $this->category]);
-
- parent::__construct($pageCall, $pageParam);
-
- $this->name = Util::ucFirst(Lang::game('objects'));
- $this->subCat = $pageParam ? '='.$pageParam : '';
- }
-
- protected function generateContent()
- {
- $this->addScript([JS_FILE, '?data=zones&locale='.User::$localeId.'&t='.$_SESSION['dataKey']]);
-
- $conditions = [];
-
- if (!User::isInGroup(U_GROUP_EMPLOYEE))
- $conditions[] = [['cuFlags', CUSTOM_EXCLUDE_FOR_LISTVIEW, '&'], 0];
-
- if ($this->category)
- $conditions[] = ['typeCat', (int)$this->category[0]];
-
- // recreate form selection
- $this->filter = $this->filterObj->getForm();
- $this->filter['query'] = $this->_get['filter'];
- $this->filter['initData'] = ['init' => 'objects'];
-
- if ($x = $this->filterObj->getSetCriteria())
- $this->filter['initData']['sc'] = $x;
-
- if ($_ = $this->filterObj->getConditions())
- $conditions[] = $_;
-
- $tabData = ['data' => []];
- $objects = new GameObjectList($conditions, ['extraOpts' => $this->filterObj->extraOpts]);
- if (!$objects->error)
- {
- $tabData['data'] = array_values($objects->getListviewData());
- if ($objects->hasSetFields(['reqSkill']))
- $tabData['visibleCols'] = ['skill'];
-
- // create note if search limit was exceeded
- if ($objects->getMatches() > CFG_SQL_LIMIT_DEFAULT)
- {
- $tabData['note'] = sprintf(Util::$tryFilteringString, 'LANG.lvnote_objectsfound', $objects->getMatches(), CFG_SQL_LIMIT_DEFAULT);
- $tabData['_truncated'] = 1;
- }
-
- if ($this->filterObj->error)
- $tabData['_errors'] = 1;
- }
-
- $this->lvTabs[] = ['object', $tabData];
- }
-
- protected function generateTitle()
- {
- array_unshift($this->title, $this->name);
- if ($this->category)
- array_unshift($this->title, Lang::gameObject('cat', $this->category[0]));
- }
-
- protected function generatePath()
- {
- if ($this->category)
- $this->path[] = $this->category[0];
- }
-}
-
-?>
diff --git a/pages/pets.php b/pages/pets.php
deleted file mode 100644
index db75efab..00000000
--- a/pages/pets.php
+++ /dev/null
@@ -1,71 +0,0 @@
-getCategoryFromUrl($pageParam);;
-
- parent::__construct($pageCall, $pageParam);
-
- $this->name = Util::ucFirst(Lang::game('pets'));
- }
-
- protected function generateContent()
- {
- $conditions = [];
-
- if (!User::isInGroup(U_GROUP_EMPLOYEE))
- $conditions[] = [['cuFlags', CUSTOM_EXCLUDE_FOR_LISTVIEW, '&'], 0];
-
- if ($this->category)
- $conditions[] = ['type', (int)$this->category[0]];
-
- $data = [];
- $pets = new PetList($conditions);
- if (!$pets->error)
- {
- $this->extendGlobalData($pets->getJSGlobals(GLOBALINFO_RELATED));
-
- $data = array(
- 'data' => array_values($pets->getListviewData()),
- 'visibleCols' => ['abilities'],
- 'computeDataFunc' => '$_'
- );
-
- if (!$pets->hasDiffFields(['type']))
- $data['hiddenCols'] = ['type'];
- };
- $this->lvTabs[] = ['pet', $data, 'petFoodCol'];
- }
-
- protected function generateTitle()
- {
- array_unshift($this->title, Util::ucFirst(Lang::game('pets')));
- if ($this->category)
- array_unshift($this->title, Lang::pet('cat', $this->category[0]));
- }
-
- protected function generatePath()
- {
- if ($this->category)
- $this->path[] = $this->category[0];
- }
-}
-
-?>
diff --git a/pages/profile.php b/pages/profile.php
deleted file mode 100644
index d3decda8..00000000
--- a/pages/profile.php
+++ /dev/null
@@ -1,253 +0,0 @@
- ['filter' => FILTER_CALLBACK, 'options' => 'GenericPage::checkDomain'],
- 'new' => ['filter' => FILTER_CALLBACK, 'options' => 'GenericPage::checkEmptySet']
- );
-
- private $isCustom = false;
- private $profile = null;
- private $rnItr = 0;
- private $powerTpl = '$WowheadPower.registerProfile(%s, %d, %s);';
-
- public function __construct($pageCall, $pageParam)
- {
- if (!CFG_PROFILER_ENABLE)
- $this->error();
-
- $params = array_map('urldecode', explode('.', $pageParam));
- if ($params[0])
- $params[0] = Profiler::urlize($params[0]);
- if (isset($params[1]))
- $params[1] = Profiler::urlize($params[1], true);
-
- parent::__construct($pageCall, $pageParam);
-
- // temp locale
- if ($this->mode == CACHE_TYPE_TOOLTIP && $this->_get['domain'])
- Util::powerUseLocale($this->_get['domain']);
-
- if (count($params) == 1 && intval($params[0]))
- {
- // redundancy much?
- $this->subjectGUID = intval($params[0]);
- $this->profile = intval($params[0]);
- $this->isCustom = true; // until proven otherwise
-
- $this->subject = new LocalProfileList(array(['id', intval($params[0])]));
- if ($this->subject->error)
- $this->notFound();
-
- if (!$this->subject->isVisibleToUser())
- $this->notFound();
-
- if (!$this->subject->isCustom())
- header('Location: '.$this->subject->getProfileUrl(), true, 302);
- }
- else if (count($params) == 3)
- {
- $this->getSubjectFromUrl($pageParam);
- if (!$this->subjectName)
- $this->notFound();
-
- // names MUST be ucFirst. Since we don't expect partial matches, search this way
- $this->profile = $params;
-
- // pending rename
- if (preg_match('/([^\-]+)-(\d+)/i', $this->subjectName, $m))
- {
- $this->subjectName = $m[1];
- $this->rnItr = $m[2];
- }
-
- // 3 possibilities
- // 1) already synced to aowow
- if ($subject = DB::Aowow()->selectRow('SELECT id, realmGUID, cuFlags FROM ?_profiler_profiles WHERE realm = ?d AND realmGUID IS NOT NULL AND name = ? AND renameItr = ?d', $this->realmId, Util::ucFirst($this->subjectName), $this->rnItr))
- {
- $this->subjectGUID = $subject['id'];
-
- if ($subject['cuFlags'] & PROFILER_CU_NEEDS_RESYNC)
- {
- $this->handleIncompleteData($params, $subject['realmGUID']);
- return;
- }
-
- $this->subject = new LocalProfileList(array(['id', $subject['id']]));
- if ($this->subject->error)
- $this->notFound();
- }
- // 2) not yet synced but exists on realm (and not a gm character)
- else if (!$this->rnItr && ($char = DB::Characters($this->realmId)->selectRow('SELECT c.guid AS realmGUID, c.name, c.race, c.class, c.level, c.gender, g.guildid AS guildGUID, IFNULL(g.name, "") AS guildName, IFNULL(gm.rank, 0) AS guildRank FROM characters c LEFT JOIN guild_member gm ON gm.guid = c.guid LEFT JOIN guild g ON g.guildid = gm.guildid WHERE c.name = ? AND level <= ?d AND (extra_flags & ?d) = 0', Util::ucFirst($this->subjectName), MAX_LEVEL, Profiler::CHAR_GMFLAGS)))
- {
- $char['realm'] = $this->realmId;
- $char['cuFlags'] = PROFILER_CU_NEEDS_RESYNC;
-
- if ($char['at_login'] & 0x1)
- $char['renameItr'] = DB::Aowow()->selectCell('SELECT MAX(renameItr) FROM ?_profiler_profiles WHERE realm = ?d AND realmGUID IS NOT NULL AND name = ?', $this->realmId, $char['name']);
-
- if ($char['guildGUID'])
- {
- // create empty guild if nessecary to satisfy foreign keys
- $char['guild'] = DB::Aowow()->selectCell('SELECT id FROM ?_profiler_guild WHERE realm = ?d AND realmGUID = ?d', $this->realmId, $char['guildGUID']);
- if (!$char['guild'])
- $char['guild'] = DB::Aowow()->query('INSERT INTO ?_profiler_guild (realm, realmGUID, cuFlags, name) VALUES (?d, ?d, ?d, ?)', $this->realmId, $char['guildGUID'], PROFILER_CU_NEEDS_RESYNC, $char['guildName']);
- }
-
- unset($char['guildGUID']);
- unset($char['guildName']);
-
- // create entry from realm with enough basic info to disply tooltips
- DB::Aowow()->query('REPLACE INTO ?_profiler_profiles (?#) VALUES (?a)', array_keys($char), array_values($char));
- $this->subjectGUID = DB::Aowow()->selectCell('SELECT id FROM ?_profiler_profiles WHERE realm = ?d AND realmGUID = ?d', $this->realmId, $char['realmGUID']);
-
- $this->handleIncompleteData($params, $char['realmGUID']);
- }
- // 3) does not exist at all
- else
- $this->notFound();
- }
- else if (($params && $params[0]) || !$this->_get['new'])
- $this->notFound();
- else if ($this->_get['new'])
- $this->mode = CACHE_TYPE_NONE;
- }
-
- protected function generateContent()
- {
- if ($this->doResync)
- return;
-
- // + .titles ?
- $this->addScript([JS_FILE, '?data=enchants.gems.glyphs.itemsets.pets.pet-talents.quick-excludes.realms.statistics.weight-presets.achievements&locale='.User::$localeId.'&t='.$_SESSION['dataKey']]);
-
- // as demanded by the raid activity tracker
- $bossIds = array(
-/* Halion */
-/* ruby */ 39863,
-/* Valanar, Lana'thel, Saurfang, Festergut, Deathwisper, Marrowgar, Putricide, Rotface, Sindragosa, Valithria, Lich King */
-/* icc */ 37970, 37955, 37813, 36626, 36855, 36612, 36678, 36627, 36853, 36789, 36597,
-/* Jaraxxus, Anub'arak */
-/* toc */ 34780, 34564,
-/* Onyxia */
-/* ony */ 10184,
-/* Flame Levi, Ignis, Razorscale, XT-002, Kologarn, Auriaya, Freya, Hodir, Mimiron, Thorim, Vezaxx, Yogg, Algalon */
-/* uld */ 33113, 33118, 33186, 33293, 32930, 33515, 32906, 32845, 33350, 32864, 33271, 33288, 32871,
-/* Anub, Faerlina, Maexxna, Noth, Heigan, Loatheb, Razuvious, Gothik, Patchwerk, Grobbulus, Gluth, Thaddius, Sapphiron, Kel'Thuzad */
-/* nax */ 15956, 15953, 15952, 15954, 15936, 16011, 16061, 16060, 16028, 15931, 15932, 15928, 15989, 15990
- );
- $this->extendGlobalIds(Type::NPC, ...$bossIds);
-
- // dummy title from dungeon encounter
- foreach (Lang::profiler('encounterNames') as $id => $name)
- $this->extendGlobalData([Type::NPC => [$id => ['name_'.User::$localeString => $name]]]);
- }
-
- protected function generatePath()
- {
-
- }
-
- protected function generateTitle()
- {
- array_unshift($this->title, Util::ucFirst(Lang::game('profile')));
- }
-
- protected function generateTooltip()
- {
- $id = $this->profile;
- if (!$this->isCustom)
- $id = "'".$this->profile[0].'.'.urlencode($this->profile[1]).'.'.urlencode($this->profile[2])."'";
-
- $power = new StdClass();
- if ($this->subject && !$this->subject->error && $this->subject->isVisibleToUser())
- {
- $n = $this->subject->getField('name');
- $l = $this->subject->getField('level');
- $r = $this->subject->getField('race');
- $c = $this->subject->getField('class');
- $g = $this->subject->getField('gender');
-
- if ($this->isCustom)
- $n .= Lang::profiler('customProfile');
- else if ($_ = $this->subject->getField('title'))
- if ($title = (new TitleList(array(['id', $_])))->getField($g ? 'female' : 'male', true))
- $n = sprintf($title, $n);
-
- $power->{'name_'.User::$localeString} = $n;
- $power->{'tooltip_'.User::$localeString} = $this->subject->renderTooltip();
- $power->icon = '$$WH.g_getProfileIcon('.$r.', '.$c.', '.$g.', '.$l.', \''.$this->subject->getIcon().'\')';
- }
-
- return sprintf($this->powerTpl, $id, User::$localeId, Util::toJSON($power, JSON_AOWOW_POWER));
- }
-
- public function display(string $override = ''): void
- {
- if ($this->mode != CACHE_TYPE_TOOLTIP)
- parent::display($override);
-
- // do not cache profile tooltips
- header(MIME_TYPE_JSON);
- die($this->generateTooltip());
- }
-
- public function notFound(string $title = '', string $msg = '') : void
- {
- parent::notFound($title ?: Util::ucFirst(Lang::profiler('profiler')), $msg ?: Lang::profiler('notFound', 'profile'));
- }
-
- private function handleIncompleteData($params, $guid)
- {
- if ($this->mode == CACHE_TYPE_TOOLTIP) // enable tooltip display with basic data we just added
- {
- $this->subject = new LocalProfileList(array(['id', $this->subjectGUID]), ['sv' => $params[1]]);
- if ($this->subject->error)
- $this->notFound();
-
- $this->profile = $params;
- }
- else // display empty page and queue status
- {
- $this->mode = CACHE_TYPE_NONE;
-
- // queue full fetch
- $newId = Profiler::scheduleResync(Type::PROFILE, $this->realmId, $guid);
-
- $this->doResync = ['profile', $newId];
- $this->initialSync();
- }
- }
-}
-
-?>
diff --git a/pages/profiler.php b/pages/profiler.php
deleted file mode 100644
index d6cf5288..00000000
--- a/pages/profiler.php
+++ /dev/null
@@ -1,37 +0,0 @@
-error();
-
- parent::__construct($pageCall, $pageParam);
- }
-
- protected function generateContent()
- {
- $this->addScript([JS_FILE, '?data=realms&locale='.User::$localeId.'&t='.$_SESSION['dataKey']]);
- }
-
- protected function generatePath() { }
-
- protected function generateTitle()
- {
- array_unshift($this->title, Util::ucFirst(Lang::profiler('profiler')));
- }
-}
-
-?>
diff --git a/pages/profiles.php b/pages/profiles.php
deleted file mode 100644
index 3b3132e7..00000000
--- a/pages/profiles.php
+++ /dev/null
@@ -1,190 +0,0 @@
- 5-man), 1 guild .. it puts a resync button on the lv...
-
- protected $type = Type::PROFILE;
-
- protected $tabId = 1;
- protected $path = [1, 5, 0];
- protected $tpl = 'profiles';
- protected $js = [[JS_FILE, 'filters.js'], [JS_FILE, 'profile_all.js'], [JS_FILE, 'profile.js']];
- protected $css = [[CSS_FILE, 'Profiler.css']];
-
- protected $_get = ['filter' => ['filter' => FILTER_UNSAFE_RAW]];
-
- public function __construct($pageCall, $pageParam)
- {
- if (!CFG_PROFILER_ENABLE)
- $this->error();
-
- $this->getSubjectFromUrl($pageParam);
-
- $realms = [];
- foreach (Profiler::getRealms() as $idx => $r)
- {
- if ($this->region && $r['region'] != $this->region)
- continue;
-
- if ($this->realm && $r['name'] != $this->realm)
- continue;
-
- $this->sumSubjects += DB::Characters($idx)->selectCell('SELECT count(*) FROM characters WHERE deleteInfos_Name IS NULL AND level <= ?d AND (extra_flags & ?) = 0', MAX_LEVEL, Profiler::CHAR_GMFLAGS);
- $realms[] = $idx;
- }
-
- $this->filterObj = new ProfileListFilter(false, ['realms' => $realms]);
-
- parent::__construct($pageCall, $pageParam);
-
- $this->name = Util::ucFirst(Lang::game('profiles'));
- $this->subCat = $pageParam ? '='.$pageParam : '';
- }
-
- protected function generateTitle()
- {
- if ($this->realm)
- array_unshift($this->title, $this->realm,/* CFG_BATTLEGROUP,*/ Lang::profiler('regions', $this->region), Lang::game('profiles'));
- else if ($this->region)
- array_unshift($this->title, Lang::profiler('regions', $this->region), Lang::game('profiles'));
- else
- array_unshift($this->title, Lang::game('profiles'));
- }
-
- protected function generateContent()
- {
- $this->addScript([JS_FILE, '?data=weight-presets.realms&locale='.User::$localeId.'&t='.$_SESSION['dataKey']]);
-
- $conditions = [];
-
- if ($_ = $this->filterObj->getConditions())
- $conditions[] = $_;
-
- if (!$this->filterObj->useLocalList)
- {
- $conditions[] = ['deleteInfos_Name', null];
- $conditions[] = ['level', MAX_LEVEL, '<=']; // prevents JS errors
- $conditions[] = [['extra_flags', Profiler::CHAR_GMFLAGS, '&'], 0];
- }
-
- // recreate form selection
- $this->filter = $this->filterObj->getForm();
- $this->filter['query'] = $this->_get['filter'];
- $this->filter['initData'] = ['init' => 'profiles'];
-
- if ($x = $this->filterObj->getSetCriteria())
- {
- $this->filter['initData']['sc'] = $x;
-
- if ($r = array_intersect([9, 12, 15, 18], $x['cr']))
- if (count($r) == 1)
- $this->roster = (reset($r) - 6) / 3; // 1, 2, 3, or 4
- }
-
- $tabData = array(
- 'id' => 'characters',
- 'hideCount' => 1,
- 'visibleCols' => ['race', 'classs', 'level', 'talents', 'achievementpoints', 'gearscore'],
- 'onBeforeCreate' => '$pr_initRosterListview' // puts a resync button on the lv
- );
-
- $extraCols = $this->filterObj->getExtraCols();
- if ($extraCols)
- {
- $xc = [];
- foreach ($extraCols as $idx => $col)
- if ($idx > 0)
- $xc[] = "\$Listview.funcBox.createSimpleCol('Skill' + ".$idx.", g_spell_skills[".$idx."], '7%', 'skill' + ".$idx.")";
-
- $tabData['extraCols'] = $xc;
- }
-
- $miscParams = [];
- if ($this->realm)
- $miscParams['sv'] = $this->realm;
- if ($this->region)
- $miscParams['rg'] = $this->region;
- if ($_ = $this->filterObj->extraOpts)
- $miscParams['extraOpts'] = $_;
-
- if ($this->filterObj->useLocalList)
- $profiles = new LocalProfileList($conditions, $miscParams);
- else
- $profiles = new RemoteProfileList($conditions, $miscParams);
-
- if (!$profiles->error)
- {
- // init these chars on our side and get local ids
- if (!$this->filterObj->useLocalList)
- $profiles->initializeLocalEntries();
-
- $addInfoMask = PROFILEINFO_CHARACTER;
-
- // init roster-listview
- // $_GET['roster'] = 1|2|3|4 originally supplemented this somehow .. 2,3,4 arenateam-size (4 => 5-man), 1 guild
- if ($this->roster == 1 && !$profiles->hasDiffFields(['guild']) && $profiles->getField('guild'))
- {
- $tabData['roster'] = $this->roster;
- $tabData['visibleCols'][] = 'guildrank';
- $tabData['hiddenCols'][] = 'guild';
-
- $this->roster = Lang::profiler('guildRoster', [$profiles->getField('guildname')]);
- }
- else if ($this->roster && !$profiles->hasDiffFields(['arenateam']) && $profiles->getField('arenateam'))
- {
- $tabData['roster'] = $this->roster;
- $tabData['visibleCols'][] = 'rating';
-
- $addInfoMask |= PROFILEINFO_ARENA;
- $this->roster = Lang::profiler('arenaRoster', [$profiles->getField('arenateam')]);
- }
- else
- $this->roster = 0;
-
- $tabData['data'] = array_values($profiles->getListviewData($addInfoMask, array_filter($extraCols, function ($x) { return $x > 0; }, ARRAY_FILTER_USE_KEY)));
-
- if ($sc = $this->filterObj->getSetCriteria())
- if (in_array(10, $sc['cr']) && !in_array('guildrank', $tabData['visibleCols']))
- $tabData['visibleCols'][] = 'guildrank';
-
- // create note if search limit was exceeded
- if ($this->filter['query'] && $profiles->getMatches() > CFG_SQL_LIMIT_DEFAULT)
- {
- $tabData['note'] = sprintf(Util::$tryFilteringString, 'LANG.lvnote_charactersfound2', $this->sumSubjects, $profiles->getMatches());
- $tabData['_truncated'] = 1;
- }
- else if ($profiles->getMatches() > CFG_SQL_LIMIT_DEFAULT)
- $tabData['note'] = sprintf(Util::$tryFilteringString, 'LANG.lvnote_charactersfound', $this->sumSubjects, 0);
-
- if ($this->filterObj->useLocalList)
- {
- if (!empty($tabData['note']))
- $tabData['note'] .= ' + "
'.Lang::profiler('complexFilter').'"';
- else
- $tabData['note'] = '
'.Lang::profiler('complexFilter').'';
- }
-
- if ($this->filterObj->error)
- $tabData['_errors'] = '$1';
- }
- else
- $this->roster = 0;
-
-
- $this->lvTabs[] = ['profile', $tabData];
-
- Lang::sort('game', 'cl');
- Lang::sort('game', 'ra');
- }
-}
-
-?>
diff --git a/pages/quests.php b/pages/quests.php
deleted file mode 100644
index 90738f3a..00000000
--- a/pages/quests.php
+++ /dev/null
@@ -1,136 +0,0 @@
- ['filter' => FILTER_UNSAFE_RAW]];
-
- public function __construct($pageCall, $pageParam)
- {
- $this->validCats = Game::$questClasses; // not allowed to set this as default
-
- $this->getCategoryFromUrl($pageParam);
- $this->filterObj = new QuestListFilter(false, ['parentCats' => $this->category]);
-
- parent::__construct($pageCall, $pageParam);
-
- $this->name = Util::ucFirst(Lang::game('quests'));
- $this->subCat = $pageParam ? '='.$pageParam : '';
- }
-
- protected function generateContent()
- {
- $conditions = [];
-
- if (!User::isInGroup(U_GROUP_EMPLOYEE))
- $conditions[] = [['cuFlags', CUSTOM_EXCLUDE_FOR_LISTVIEW, '&'], 0];
-
- if (isset($this->category[1]))
- $conditions[] = ['zoneOrSort', $this->category[1]];
- else if (isset($this->category[0]))
- $conditions[] = ['zoneOrSort', $this->validCats[$this->category[0]]];
-
- if ($_ = $this->filterObj->getConditions())
- $conditions[] = $_;
-
- $quests = new QuestList($conditions, ['extraOpts' => $this->filterObj->extraOpts]);
-
- $this->extendGlobalData($quests->getJSGlobals());
-
- // recreate form selection
- $this->filter = $this->filterObj->getForm();
- $this->filter['query'] = $this->_get['filter'];
- $this->filter['initData'] = ['init' => 'quests'];
-
- $rCols = $this->filterObj->getReputationCols();
- $xCols = $this->filterObj->getExtraCols();
- if ($rCols)
- $this->filter['initData']['rc'] = $rCols;
-
- if ($xCols)
- $this->filter['initData']['ec'] = $xCols;
-
- if ($x = $this->filterObj->getSetCriteria())
- $this->filter['initData']['sc'] = $x;
-
- $tabData = ['data' => array_values($quests->getListviewData())];
-
- if ($rCols)
- $tabData['extraCols'] = '$fi_getReputationCols('.json_encode($rCols, JSON_NUMERIC_CHECK | JSON_UNESCAPED_UNICODE).')';
- else if ($xCols)
- $tabData['extraCols'] = '$fi_getExtraCols(fi_extraCols, 0, 0)';
-
- // create note if search limit was exceeded
- if ($quests->getMatches() > CFG_SQL_LIMIT_DEFAULT)
- {
- $tabData['note'] = sprintf(Util::$tryFilteringString, 'LANG.lvnote_questsfound', $quests->getMatches(), CFG_SQL_LIMIT_DEFAULT);
- $tabData['_truncated'] = 1;
- }
- else if (isset($this->category[1]) && $this->category[1] > 0)
- $tabData['note'] = '$$WH.sprintf(LANG.lvnote_questgivers, '.$this->category[1].', g_zones['.$this->category[1].'], '.$this->category[1].')';
-
- if ($this->filterObj->error)
- $tabData['_errors'] = 1;
-
- $this->lvTabs[] = ['quest', $tabData];
- }
-
- protected function generateTitle()
- {
- array_unshift($this->title, $this->name);
-
- if (isset($this->category[1]))
- array_unshift($this->title, Lang::quest('cat', $this->category[0], $this->category[1]));
- else if (isset($this->category[0]))
- {
- $c0 = Lang::quest('cat', $this->category[0]);
- array_unshift($this->title, is_array($c0) ? $c0[0] : $c0);
- }
- }
-
- protected function generatePath()
- {
- foreach ($this->category as $c)
- $this->path[] = $c;
-
- $hubs = array(
- // Quest Hubs
- 3679 => 3519, 4024 => 3537, 25 => 46, 1769 => 361,
- // Startzones: Horde
- 132 => 1, 9 => 12, 3431 => 3430, 154 => 85,
- // Startzones: Alliance
- 3526 => 3524, 363 => 14, 220 => 215, 188 => 141,
- // Group: Caverns of Time
- 2366 => 1941, 2367 => 1941, 4100 => 1941,
- // Group: Hellfire Citadell
- 3562 => 3535, 3713 => 3535, 3714 => 3535,
- // Group: Auchindoun
- 3789 => 3688, 3790 => 3688, 3791 => 3688, 3792 => 3688,
- // Group: Tempest Keep
- 3847 => 3842, 3848 => 3842, 3849 => 3842,
- // Group: Coilfang Reservoir
- 3715 => 3905, 3716 => 3905, 3717 => 3905,
- // Group: Icecrown Citadel
- 4809 => 4522, 4813 => 4522, 4820 => 4522
- );
-
- if (isset($this->category[1]) && isset($hubs[$this->category[1]]))
- array_splice($this->path, 3, 0, $hubs[$this->category[1]]);
- }
-}
-
-?>
diff --git a/pages/race.php b/pages/race.php
deleted file mode 100644
index fb369710..00000000
--- a/pages/race.php
+++ /dev/null
@@ -1,208 +0,0 @@
-typeId = intVal($id);
-
- $this->subject = new CharRaceList(array(['id', $this->typeId]));
- if ($this->subject->error)
- $this->notFound(Lang::game('race'), Lang::race('notFound'));
-
- $this->name = $this->subject->getField('name', true);
- }
-
- protected function generatePath()
- {
- $this->path[] = $this->typeId;
- }
-
- protected function generateTitle()
- {
- array_unshift($this->title, $this->subject->getField('name', true), Util::ucFirst(Lang::game('class')));
- }
-
- protected function generateContent()
- {
- $infobox = [];
- $_mask = 1 << ($this->typeId - 1);
- $mountVendors = array( // race => [starter, argent tournament]
- null, [384, 33307], [3362, 33553], [1261, 33310],
- [4730, 33653], [4731, 33555], [3685, 33556], [7955, 33650],
- [7952, 33554], null, [16264, 33557], [17584, 33657]
- );
-
- /***********/
- /* Infobox */
- /***********/
-
- $infobox = Lang::getInfoBoxForFlags($this->subject->getField('cuFlags'));
-
- // side
- if ($_ = $this->subject->getField('side'))
- $infobox[] = Lang::main('side').Lang::main('colon').'[span class=icon-'.($_ == 2 ? 'horde' : 'alliance').']'.Lang::game('si', $_).'[/span]';
-
- // faction
- if ($_ = $this->subject->getField('factionId'))
- {
- $fac = new FactionList(array(['f.id', $_]));
- $this->extendGlobalData($fac->getJSGlobals());
- $infobox[] = Util::ucFirst(Lang::game('faction')).Lang::main('colon').'[faction='.$fac->id.']';
- }
-
- // leader
- if ($_ = $this->subject->getField('leader'))
- {
- $this->extendGlobalIds(Type::NPC, $_);
- $infobox[] = Lang::race('racialLeader').Lang::main('colon').'[npc='.$_.']';
- }
-
- // start area
- if ($_ = $this->subject->getField('startAreaId'))
- {
- $this->extendGlobalIds(Type::ZONE, $_);
- $infobox[] = Lang::race('startZone').Lang::main('colon').'[zone='.$_.']';
- }
-
-
- /****************/
- /* Main Content */
- /****************/
-
- $this->infobox = '[ul][li]'.implode('[/li][li]', $infobox).'[/li][/ul]';
- $this->expansion = Util::$expansionString[$this->subject->getField('expansion')];
- $this->headIcons = array(
- 'race_'.strtolower($this->subject->getField('fileString')).'_male',
- 'race_'.strtolower($this->subject->getField('fileString')).'_female'
- );
- $this->redButtons = array(
- BUTTON_WOWHEAD => true,
- BUTTON_LINKS => ['type' => $this->type, 'typeId' => $this->typeId]
- );
-
-
- /**************/
- /* Extra Tabs */
- /**************/
-
- // Classes
- $classes = new CharClassList(array(['racemask', $_mask, '&']));
- if (!$classes->error)
- {
- $this->extendGlobalData($classes->getJSGlobals());
- $this->lvTabs[] = ['class', ['data' => array_values($classes->getListviewData())]];
- }
-
- // Tongues
- $conditions = array(
- ['typeCat', -11], // proficiencies
- ['reqRaceMask', $_mask, '&'] // only languages are race-restricted
- );
-
- $tongues = new SpellList($conditions);
- if (!$tongues->error)
- {
- $this->extendGlobalData($tongues->getJSGlobals());
- $this->lvTabs[] = ['spell', array(
- 'data' => array_values($tongues->getListviewData()),
- 'id' => 'languages',
- 'name' => '$LANG.tab_languages',
- 'hiddenCols' => ['reagents']
- )];
- }
-
- // Racials
- $conditions = array(
- ['typeCat', -4], // racial traits
- ['reqRaceMask', $_mask, '&']
- );
-
- $racials = new SpellList($conditions);
- if (!$racials->error)
- {
- $this->extendGlobalData($racials->getJSGlobals());
- $this->lvTabs[] = ['spell', array(
- 'data' => array_values($racials->getListviewData()),
- 'id' => 'racial-traits',
- 'name' => '$LANG.tab_racialtraits',
- 'hiddenCols' => ['reagents']
- )];
- }
-
- // Quests
- $conditions = array(
- ['reqRaceMask', $_mask, '&'],
- [['reqRaceMask', RACE_MASK_HORDE, '&'], RACE_MASK_HORDE, '!'],
- [['reqRaceMask', RACE_MASK_ALLIANCE, '&'], RACE_MASK_ALLIANCE, '!']
- );
-
- $quests = new QuestList($conditions);
- if (!$quests->error)
- {
- $this->extendGlobalData($quests->getJSGlobals());
- $this->lvTabs[] = ['quest', ['data' => array_values($quests->getListviewData())]];
- }
-
- // Mounts
- // ok, this sucks, but i rather hardcode the trainer, than fetch items by namepart
- $items = isset($mountVendors[$this->typeId]) ? DB::World()->selectCol('SELECT item FROM npc_vendor WHERE entry IN (?a)', $mountVendors[$this->typeId]) : 0;
-
- $conditions = array(
- ['i.id', $items],
- ['i.class', ITEM_CLASS_MISC],
- ['i.subClass', 5], // mounts
- );
-
- $mounts = new ItemList($conditions);
- if (!$mounts->error)
- {
- $this->extendGlobalData($mounts->getJSGlobals());
- $this->lvTabs[] = ['item', array(
- 'data' => array_values($mounts->getListviewData()),
- 'id' => 'mounts',
- 'name' => '$LANG.tab_mounts',
- 'hiddenCols' => ['slot', 'type']
- )];
- }
-
- // Sounds
- if ($vo = DB::Aowow()->selectCol('SELECT soundId AS ARRAY_KEY, gender FROM ?_races_sounds WHERE raceId = ?d', $this->typeId))
- {
- $sounds = new SoundList(array(['id', array_keys($vo)]));
- if (!$sounds->error)
- {
- $this->extendGlobalData($sounds->getJSGlobals(GLOBALINFO_SELF));
- $data = $sounds->getListviewData();
- foreach ($data as $id => &$d)
- $d['gender'] = $vo[$id];
-
- $this->lvTabs[] = ['sound', array(
- 'data' => array_values($data),
- 'extraCols' => ['$Listview.templates.title.columns[1]']
- )];
- }
- }
- }
-}
-
-
-?>
diff --git a/pages/races.php b/pages/races.php
deleted file mode 100644
index 76621a11..00000000
--- a/pages/races.php
+++ /dev/null
@@ -1,49 +0,0 @@
-name = Util::ucFirst(Lang::game('races'));
- }
-
- protected function generateContent()
- {
- $conditions = [];
-
- if (!User::isInGroup(U_GROUP_EMPLOYEE))
- $conditions[] = [['cuFlags', CUSTOM_EXCLUDE_FOR_LISTVIEW, '&'], 0];
-
- $data = [];
- $races = new CharRaceList($conditions);
- if (!$races->error)
- $data = array_values($races->getListviewData());
-
- $this->lvTabs[] = ['race', ['data' => $data]];
- }
-
- protected function generateTitle()
- {
- array_unshift($this->title, Util::ucFirst(Lang::game('races')));
- }
-
- protected function generatePath() {}
-}
-
-?>
diff --git a/pages/screenshot.php b/pages/screenshot.php
deleted file mode 100644
index 6335b42f..00000000
--- a/pages/screenshot.php
+++ /dev/null
@@ -1,343 +0,0 @@
-[_original].jpg
-
-class ScreenshotPage extends GenericPage
-{
- const MAX_W = 488;
- const MAX_H = 325;
-
- protected $tpl = 'screenshot';
- protected $js = [[JS_FILE, 'Cropper.js']];
- protected $css = [[CSS_FILE, 'Cropper.css']];
- protected $reqAuth = true;
- protected $tabId = 0;
-
- private $tmpPath = 'static/uploads/temp/';
- private $pendingPath = 'static/uploads/screenshots/pending/';
- private $destination = null;
- private $minSize = CFG_SCREENSHOT_MIN_SIZE;
-
- protected $validCats = ['add', 'crop', 'complete', 'thankyou'];
- protected $destType = 0;
- protected $destTypeId = 0;
- protected $imgHash = '';
-
- protected $_post = array(
- 'coords' => ['filter' => FILTER_CALLBACK, 'options' => 'GenericPage::checkIdListUnsigned'],
- 'screenshotalt' => ['filter' => FILTER_UNSAFE_RAW, 'flags' => FILTER_FLAG_STRIP_AOWOW]
- );
-
- public function __construct($pageCall, $pageParam)
- {
- parent::__construct($pageCall, $pageParam);
-
- $this->name = Lang::screenshot('submission');
- $this->command = $pageParam;
-
- if ($this->minSize <= 0)
- {
- trigger_error('config error: dimensions for uploaded screenshots equal or less than zero. Value forced to 200', E_USER_WARNING);
- $this->minSize = 200;
- }
-
- // get screenshot destination
- // target delivered as screenshot=
&.. (hash is optional)
- if (preg_match('/^screenshot=\w+&(-?\d+)\.(-?\d+)(\.(\w{16}))?$/i', $_SERVER['QUERY_STRING'] ?? '', $m))
- {
- // no such type or this type cannot receive screenshots
- if (!Type::checkClassAttrib($m[1], 'contribute', CONTRIBUTE_SS))
- $this->error();
-
- $this->destination = Type::newList($m[1], [['id', intVal($m[2])]]);
-
- // no such typeId
- if (!$this->destination || $this->destination->error)
- $this->error();
-
- // only accept/expect hash for crop & complete
- if (empty($m[4]) && ($this->command == 'crop' || $this->command == 'complete'))
- $this->error();
- else if (!empty($m[4]) && ($this->command == 'add' || $this->command == 'thankyou'))
- $this->error();
- else if (!empty($m[4]))
- $this->imgHash = $m[4];
-
- $this->destType = intVal($m[1]);
- $this->destTypeId = intVal($m[2]);
- }
- else
- $this->error();
- }
-
- protected function generateContent() : void
- {
- switch ($this->command)
- {
- case 'add':
- if ($this->handleAdd())
- header('Location: ?screenshot=crop&'.$this->destType.'.'.$this->destTypeId.'.'.$this->imgHash, true, 302);
- else
- header('Location: ?'.Type::getFileString($this->destType).'='.$this->destTypeId.'#submit-a-screenshot', true, 302);
- die();
- case 'crop':
- $this->handleCrop();
- break;
- case 'complete':
- if ($_ = $this->handleComplete())
- $this->notFound(Lang::main('nfPageTitle'), sprintf(Lang::main('intError2'), '#'.$_));
- else
- header('Location: ?screenshot=thankyou&'.$this->destType.'.'.$this->destTypeId, true, 302);
- die();
- case 'thankyou':
- $this->tpl = 'list-page-generic';
- $this->handleThankyou();
- break;
- }
- }
-
-
- /*******************/
- /* command handler */
- /*******************/
-
- private function handleAdd() : bool
- {
- $this->imgHash = Util::createHash(16);
-
- if (!User::canUploadScreenshot())
- {
- $_SESSION['error']['ss'] = Lang::screenshot('error', 'notAllowed');
- return false;
- }
-
- if ($_ = $this->validateScreenshot($isPNG))
- {
- $_SESSION['error']['ss'] = $_;
- return false;
- }
-
- $im = $isPNG ? $this->loadFromPNG() : $this->loadFromJPG();
- if (!$im)
- {
- $_SESSION['error']['ss'] = Lang::main('intError');
- return false;
- }
-
- $oSize = $rSize = [imagesx($im), imagesy($im)];
- $rel = $oSize[0] / $oSize[1];
-
- // check for oversize and refit to crop-screen
- if ($rel >= 1.5 && $oSize[0] > self::MAX_W)
- $rSize = [self::MAX_W, self::MAX_W / $rel];
- else if ($rel < 1.5 && $oSize[1] > self::MAX_H)
- $rSize = [self::MAX_H * $rel, self::MAX_H];
-
- // use this image for work
- $this->writeImage($im, $oSize[0], $oSize[1], $this->ssName().'_original');
- // use this image to display
- $this->writeImage($im, $rSize[0], $rSize[1], $this->ssName());
-
- return true;
- }
-
- private function handleCrop() : void
- {
- $im = imagecreatefromjpeg($this->tmpPath.$this->ssName().'_original.jpg');
-
- $oSize = $rSize = [imagesx($im), imagesy($im)];
- $rel = $oSize[0] / $oSize[1];
-
- // check for oversize and refit to crop-screen
- if ($rel >= 1.5 && $oSize[0] > self::MAX_W)
- $rSize = [self::MAX_W, self::MAX_W / $rel];
- else if ($rel < 1.5 && $oSize[1] > self::MAX_H)
- $rSize = [self::MAX_H * $rel, self::MAX_H];
-
- // r: resized; o: original
- // r: x <= 488 && y <= 325 while x proportional to y
- // mincrop is optional and specifies the minimum resulting image size
- $this->cropper = [
- 'url' => STATIC_URL.'/uploads/temp/'.$this->ssName().'.jpg',
- 'parent' => 'ss-container',
- 'oWidth' => $oSize[0],
- 'rWidth' => $rSize[0],
- 'oHeight' => $oSize[1],
- 'rHeight' => $rSize[1],
- 'type' => $this->destType, // only used to check against NPC: 15384 [OLDWorld Trigger (DO NOT DELETE)]
- 'typeId' => $this->destTypeId // i guess this was used to upload arbitrary imagery
- ];
-
- // minimum dimensions
- if (!User::isInGroup(U_GROUP_STAFF))
- $this->cropper['minCrop'] = $this->minSize;
-
- // target
- $this->infobox = sprintf(Lang::screenshot('displayOn'), Lang::typeName($this->destType), Type::getFileString($this->destType), $this->destTypeId);
- $this->extendGlobalIds($this->destType, $this->destTypeId);
- }
-
- private function handleComplete() : int
- {
- // check tmp file
- $fullPath = $this->tmpPath.$this->ssName().'_original.jpg';
- if (!file_exists($fullPath))
- return 1;
-
- // check post data
- if (!$this->_post['coords'])
- return 2;
-
- $dims = $this->_post['coords'];
- if (count($dims) != 4)
- return 3;
-
- Util::checkNumeric($dims, NUM_REQ_INT);
-
- // actually crop the image
- $srcImg = imagecreatefromjpeg($fullPath);
-
- $x = (int)(imagesx($srcImg) * $dims[0]);
- $y = (int)(imagesy($srcImg) * $dims[1]);
- $w = (int)(imagesx($srcImg) * $dims[2]);
- $h = (int)(imagesy($srcImg) * $dims[3]);
-
- $destImg = imagecreatetruecolor($w, $h);
-
- imagefill($destImg, 0, 0, imagecolorallocate($destImg, 255, 255, 255));
- imagecopy($destImg, $srcImg, 0, 0, $x, $y, $w, $h);
- imagedestroy($srcImg);
-
- // write to db
- $newId = DB::Aowow()->query(
- 'INSERT INTO ?_screenshots (type, typeId, userIdOwner, date, width, height, caption) VALUES (?d, ?d, ?d, UNIX_TIMESTAMP(), ?d, ?d, ?)',
- $this->destType, $this->destTypeId,
- User::$id,
- $w, $h,
- $this->_post['screenshotalt'] ?? ''
- );
-
- // write to file
- if (is_int($newId)) // 0 is valid, NULL or FALSE is not
- imagejpeg($destImg, $this->pendingPath.$newId.'.jpg', 100);
- else
- return 6;
-
- return 0;
- }
-
- private function handleThankyou() : void
- {
- $this->extraHTML = Lang::screenshot('thanks', 'contrib').'
';
- $this->extraHTML .= sprintf(Lang::screenshot('thanks', 'goBack'), Type::getFileString($this->destType), $this->destTypeId)."
\n";
- $this->extraHTML .= ''.Lang::screenshot('thanks', 'note').'';
- }
-
-
- /**********/
- /* helper */
- /**********/
-
- private function loadFromPNG() // : resource/gd
- {
- $image = imagecreatefrompng($_FILES['screenshotfile']['tmp_name']);
- $bg = imagecreatetruecolor(imagesx($image), imagesy($image));
-
- imagefill($bg, 0, 0, imagecolorallocate($bg, 255, 255, 255));
- imagealphablending($bg, true);
- imagecopy($bg, $image, 0, 0, 0, 0, imagesx($image), imagesy($image));
- imagedestroy($image);
-
- return $bg;
- }
-
- private function loadFromJPG() // : resource/gd
- {
- return imagecreatefromjpeg($_FILES['screenshotfile']['tmp_name']);
- }
-
- private function writeImage(/*resource/gd*/ $im, int $w, int $h, string $file) : bool
- {
- if ($res = imagecreatetruecolor($w, $h))
- if (imagecopyresampled($res, $im, 0, 0, 0, 0, $w, $h, imagesx($im), imagesy($im)))
- if (imagejpeg($res, $this->tmpPath.$file.'.jpg', 100))
- return true;
-
- return false;
- }
-
- private function validateScreenshot(?bool &$isPNG = false) : string
- {
- // no upload happened or some error occured
- if (!$_FILES || empty($_FILES['screenshotfile']))
- return Lang::screenshot('error', 'selectSS');
-
- switch ($_FILES['screenshotfile']['error']) // 0 is fine
- {
- case UPLOAD_ERR_INI_SIZE: // 1
- case UPLOAD_ERR_FORM_SIZE: // 2
- trigger_error('validateScreenshot - the file exceeds the maximum size of '.ini_get('upload_max_filesize'), E_USER_WARNING);
- return Lang::screenshot('error', 'selectSS');
- case UPLOAD_ERR_PARTIAL: // 3
- trigger_error('validateScreenshot - upload was interrupted', E_USER_WARNING);
- return Lang::screenshot('error', 'selectSS');
- case UPLOAD_ERR_NO_FILE: // 4
- trigger_error('validateScreenshot - no file was received', E_USER_WARNING);
- return Lang::screenshot('error', 'selectSS');
- case UPLOAD_ERR_NO_TMP_DIR: // 6
- trigger_error('validateScreenshot - temporary upload directory is not set', E_USER_ERROR);
- return Lang::main('intError');
- case UPLOAD_ERR_CANT_WRITE: // 7
- trigger_error('validateScreenshot - could not write temporary file to disk', E_USER_ERROR);
- return Lang::main('intError');
- case UPLOAD_ERR_EXTENSION: // 8
- trigger_error('validateScreenshot - a php extension stopped the file upload.', E_USER_ERROR);
- return Lang::main('intError');
- }
-
- // points to invalid file (hack attempt)
- if (!is_uploaded_file($_FILES['screenshotfile']['tmp_name']))
- {
- trigger_error('validateScreenshot - uploaded file not in upload directory', E_USER_ERROR);
- return Lang::main('intError');
- }
-
- // check if file is an image; allow jpeg, png
- $finfo = new finfo(FILEINFO_MIME); // fileInfo appends charset information and other nonsense
- $mime = $finfo->file($_FILES['screenshotfile']['tmp_name']);
- if (preg_match('/^image\/(png|jpe?g)/i', $mime, $m))
- $isPNG = $m[0] == 'image/png';
- else
- return Lang::screenshot('error', 'unkFormat');
-
- // invalid file
- $is = getimagesize($_FILES['screenshotfile']['tmp_name']);
- if (!$is)
- return Lang::screenshot('error', 'selectSS');
-
- // size-missmatch: 4k UHD upper limit; 150px lower limit
- if ($is[0] < $this->minSize || $is[1] < $this->minSize)
- return Lang::screenshot('error', 'tooSmall');
- else if ($is[0] > 3840 || $is[1] > 2160)
- return Lang::screenshot('error', 'selectSS');
-
- return '';
- }
-
- private function ssName() : string
- {
- return $this->imgHash ? User::$displayName.'-'.$this->destType.'-'.$this->destTypeId.'-'.$this->imgHash : '';
- }
-
- protected function generatePath() : void { }
- protected function generateTitle() : void
- {
- array_unshift($this->title, Lang::screenshot('submission'));
- }
-}
-
-?>
diff --git a/pages/search.php b/pages/search.php
deleted file mode 100644
index 97243867..00000000
--- a/pages/search.php
+++ /dev/null
@@ -1,1398 +0,0 @@
- search by compare or profiler
- else if &opensearch
- => suggestions when typing into searchboxes
- array:[
- str, // search
- str[10], // found
- [], // unused
- [], // unused
- [], // unused
- [], // unused
- [], // unused
- str[10][4] // type, typeId, param1 (4:quality, 3,6,9,10,17:icon, 5,11:faction), param2 (3:quality, 6:rank)
- ]
- else
- => listviews
-*/
-
-
-// tabId 0: Database g_initHeader()
-class SearchPage extends GenericPage
-{
- protected $tpl = 'search';
- protected $tabId = 0;
- protected $mode = CACHE_TYPE_SEARCH;
- protected $js = [[JS_FILE, 'swfobject.js']];
- protected $lvTabs = []; // [file, data, extraInclude, osInfo] // osInfo:[type, appendix, nMatches, param1, param2]
- protected $forceTabs = true;
- protected $search = ''; // output
- protected $invalid = [];
-
- protected $_get = array(
- 'wt' => ['filter' => FILTER_CALLBACK, 'options' => 'GenericPage::checkIntArray'],
- 'wtv' => ['filter' => FILTER_CALLBACK, 'options' => 'GenericPage::checkIntArray'],
- 'slots' => ['filter' => FILTER_CALLBACK, 'options' => 'GenericPage::checkIntArray'],
- 'type' => ['filter' => FILTER_CALLBACK, 'options' => 'GenericPage::checkInt'],
- 'json' => ['filter' => FILTER_CALLBACK, 'options' => 'GenericPage::checkEmptySet'],
- 'opensearch' => ['filter' => FILTER_CALLBACK, 'options' => 'GenericPage::checkEmptySet']
- );
-
- private $maxResults = CFG_SQL_LIMIT_SEARCH;
- private $searchMask = 0x0;
- private $query = ''; // lookup
- private $included = [];
- private $excluded = [];
- private $statWeights = [];
- private $searches = array(
- '_searchCharClass', '_searchCharRace', '_searchTitle', '_searchWorldEvent', '_searchCurrency',
- '_searchItemset', '_searchItem', '_searchAbility', '_searchTalent', '_searchGlyph',
- '_searchProficiency', '_searchProfession', '_searchCompanion', '_searchMount', '_searchCreature',
- '_searchQuest', '_searchAchievement', '_searchStatistic', '_searchZone', '_searchObject',
- '_searchFaction', '_searchSkill', '_searchPet', '_searchCreatureAbility', '_searchSpell',
- '_searchEmote', '_searchEnchantment', '_searchSound'
- );
-
- public function __construct($pageCall, $pageParam)
- {
- // restricted access
- if ($this->reqUGroup && !User::isInGroup($this->reqUGroup))
- $this->error();
-
- parent::__construct($pageCall, $pageParam); // just to set g_user and g_locale
-
- $this->search =
- $this->query = trim(urlDecode($pageParam));
-
- // sanitize stat weights
- if ($this->_get['wt'] && $this->_get['wtv'])
- {
- $wt = $this->_get['wt'];
- $wtv = $this->_get['wtv'];
- $nwt = count($wt);
- $nwtv = count($wtv);
-
- if ($nwt > $nwtv)
- array_splice($wt, $nwtv);
- else if ($nwtv > $nwt)
- array_splice($wtv, $nwt);
-
- if ($wt && $wtv)
- $this->statWeights = [$wt, $wtv];
- }
-
- // select search mode
- if ($this->_get['json'])
- {
- if ($_ = intVal($this->search)) // allow for search by Id
- $this->query = $_;
-
- if ($this->_get['slots'])
- $this->searchMask |= SEARCH_TYPE_JSON | 0x40;
- else if ($this->_get['type'] == Type::ITEMSET)
- $this->searchMask |= SEARCH_TYPE_JSON | 0x60;
- else if ($this->_get['type'] == Type::ITEM)
- $this->searchMask |= SEARCH_TYPE_JSON | 0x40;
- }
- else if ($this->_get['opensearch'])
- {
- $this->maxResults = CFG_SQL_LIMIT_QUICKSEARCH;
- $this->searchMask |= SEARCH_TYPE_OPEN | SEARCH_MASK_OPEN;
- }
- else
- $this->searchMask |= SEARCH_TYPE_REGULAR | SEARCH_MASK_ALL;
-
- // handle maintenance status for js-cases
- if (CFG_MAINTENANCE && !User::isInGroup(U_GROUP_EMPLOYEE) && !($this->searchMask & SEARCH_TYPE_REGULAR))
- $this->notFound();
-
- // fill include, exclude and ignore
- $this->tokenizeQuery();
-
- // invalid conditions: not enough characters to search OR no types to search
- if ((CFG_MAINTENANCE && !User::isInGroup(U_GROUP_EMPLOYEE)) ||
- (!$this->included && ($this->searchMask & (SEARCH_TYPE_OPEN | SEARCH_TYPE_REGULAR))) ||
- (($this->searchMask & SEARCH_TYPE_JSON) && !$this->included && !$this->statWeights))
- {
- $this->mode = CACHE_TYPE_NONE;
- $this->notFound();
- }
- }
-
- private function tokenizeQuery()
- {
- if (!$this->query)
- return;
-
- foreach (explode(' ', $this->query) as $raw)
- {
- $clean = str_replace(['\\', '%'], '', $raw);
-
- if (!$clean) // multiple spaces
- continue;
- else if ($clean[0] == '-')
- {
- if (mb_strlen($clean) < 4)
- $this->invalid[] = mb_substr($raw, 1);
- else
- $this->excluded[] = mb_substr(str_replace('_', '\\_', $clean), 1);
- }
- else if ($clean !== '')
- {
- if (mb_strlen($clean) < 3)
- $this->invalid[] = $raw;
- else
- $this->included[] = str_replace('_', '\\_', $clean);
- }
- }
- }
-
- protected function generateCacheKey($withStaff = true)
- {
- $staff = intVal($withStaff && User::isInGroup(U_GROUP_EMPLOYEE));
-
- $key = [$this->mode, $this->searchMask, md5($this->query), $staff, User::$localeId];
-
- return implode('_', $key);
- }
-
- protected function postCache()
- {
- if (!empty($this->lvTabs[3])) // has world events
- {
- // update WorldEvents to date()
- foreach ($this->lvTabs[3][1]['data'] as &$d)
- {
- $updated = WorldEventList::updateDates($d['_date']);
- unset($d['_date']);
- $d['startDate'] = $updated['start'] ? date(Util::$dateFormatInternal, $updated['start']) : false;
- $d['endDate'] = $updated['end'] ? date(Util::$dateFormatInternal, $updated['end']) : false;
- $d['rec'] = $updated['rec'];
- }
- }
-
- if ($this->searchMask & SEARCH_TYPE_REGULAR)
- {
- $foundTotal = 0;
- foreach ($this->lvTabs as [$file, $tabData, , $osInfo])
- $foundTotal += count($tabData['data']);
-
- if ($foundTotal == 1) // only one match -> redirect to find
- {
- $tab = array_pop($this->lvTabs);
- $type = Type::getFileString($tab[3][0]);
- $typeId = array_pop($tab[1]['data'])['id'];
-
- header('Location: ?'.$type.'='.$typeId, true, 302);
- exit();
- }
- }
- }
-
- protected function generateTitle()
- {
- array_unshift($this->title, $this->search, Lang::main('search'));
- }
-
- protected function generatePath() { }
-
- protected function generateContent() // just wrap it, so GenericPage can call and cache it
- {
- if ($this->mode == CACHE_TYPE_NONE) // search is invalid
- return;
-
- $this->addScript([JS_FILE, '?data=zones&locale='.User::$localeId.'&t='.$_SESSION['dataKey']]);
-
- $this->performSearch();
- }
-
- public function notFound(string $title = '', string $msg = '') : void
- {
- if ($this->searchMask & SEARCH_TYPE_REGULAR)
- {
- // empty queries go home
- if (!$this->query)
- {
- header('Location: .', true, 302);
- die();
- }
-
- parent::display(); // errors are handled in the search-template itself
- }
- else if ($this->searchMask & SEARCH_TYPE_OPEN)
- $result = [$this->search, []];
- else if ($this->searchMask & SEARCH_TYPE_JSON)
- $result = [$this->search, [], []];
-
- header(MIME_TYPE_JSON);
- exit(Util::toJSON($result));
- }
-
- public function display(string $override = '') : void
- {
- if ($override || ($this->searchMask & SEARCH_TYPE_REGULAR))
- parent::display($override);
- else if ($this->searchMask & SEARCH_TYPE_OPEN)
- $this->displayExtra([$this, 'generateOpenSearch']);
- else if ($this->searchMask & SEARCH_TYPE_JSON)
- $this->displayExtra([$this, 'generateJsonSearch']);
- }
-
- // !note! dear reader, if you ever try to generate a string, that is to be evaled by JS, NEVER EVER terminate with a \n ..... $totalHoursWasted +=2;
- protected function generateJsonSearch()
- {
- $outItems = [];
- $outSets = [];
-
- $this->performSearch();
-
- // items
- if (!empty($this->lvTabs[6][1]['data']))
- $outItems = array_values($this->lvTabs[6][1]['data']);
-
- // item sets
- if (!empty($this->lvTabs[5][1]['data']))
- {
- foreach ($this->lvTabs[5][1]['data'] as $k => $v)
- {
- unset($v['quality']);
- if (!$v['heroic'])
- unset($v['heroic']);
-
- $outSets[] = $v;
- }
- }
-
- return Util::toJSON([$this->search, $outItems, $outSets]);
- }
-
- protected function generateOpenSearch()
- {
- // this one is funny: we want 10 results, ideally equally distributed over each type
- $foundTotal = 0;
- $limit = $this->maxResults;
- $result = array( //idx1: names, idx3: resultUrl; idx7: extraInfo
- $this->search,
- [], [], [], [], [], [], []
- );
-
- $this->performSearch();
-
- foreach ($this->lvTabs as [ , , , $osInfo])
- $foundTotal += $osInfo[2];
-
- if (!$foundTotal)
- return Util::toJSON([$this->search, []]);
-
- foreach ($this->lvTabs as [ , $tabData, , $osInfo])
- {
- $max = max(1, intVal($limit * $osInfo[2] / $foundTotal));
- $limit -= $max;
-
- for ($i = 0; $i < $max; $i++)
- {
- $data = array_shift($tabData['data']);
- if (!$data)
- break;
-
- $hasQ = is_numeric($data['name'][0]) || $data['name'][0] == '@';
- $result[1][] = ($hasQ ? mb_substr($data['name'], 1) : $data['name']).$osInfo[1];
- $result[3][] = HOST_URL.'/?'.Type::getFileString($osInfo[0]).'='.$data['id'];
-
- $extra = [$osInfo[0], $data['id']]; // type, typeId
-
- if (isset($osInfo[3][$data['id']]))
- $extra[] = $osInfo[3][$data['id']]; // param1
-
- if (isset($osInfo[4][$data['id']]))
- $extra[] = $osInfo[4][$data['id']]; // param2
-
- $result[7][] = $extra;
- }
-
- if ($limit <= 0)
- break;
- }
-
- return Util::toJSON($result);
- }
-
- private function createLookup(array $fields = [])
- {
- // default to name-field
- if (!$fields)
- $fields[] = 'name_loc'.User::$localeId;
-
- $qry = [];
- foreach ($fields as $f)
- {
- $sub = [];
- foreach ($this->included as $i)
- $sub[] = [$f, '%'.$i.'%'];
-
- foreach ($this->excluded as $x)
- $sub[] = [$f, '%'.$x.'%', '!'];
-
- // single cnd?
- if (count($sub) > 1)
- array_unshift($sub, 'AND');
- else
- $sub = $sub[0];
-
- $qry[] = $sub;
- }
-
- // single cnd?
- if (count($qry) > 1)
- array_unshift($qry, 'OR');
- else
- $qry = $qry[0];
-
- return $qry;
- }
-
- private function performSearch()
- {
- $cndBase = ['AND', $this->maxResults];
-
- // Exclude internal wow stuff
- if (!User::isInGroup(U_GROUP_EMPLOYEE))
- $cndBase[] = [['cuFlags', CUSTOM_EXCLUDE_FOR_LISTVIEW, '&'], 0];
-
- $shared = [];
- foreach ($this->searches as $idx => $ref)
- if ($this->searchMask & (1 << $idx))
- if ($_ = $this->$ref($cndBase, $shared))
- $this->lvTabs[$idx] = $_;
- }
-
- private function _searchCharClass($cndBase) // 0 Classes: $searchMask & 0x00000001
- {
- $cnd = array_merge($cndBase, [$this->createLookup()]);
- $classes = new CharClassList($cnd);
-
- if ($data = $classes->getListviewData())
- {
- $result['data'] = array_values($data);
- $osInfo = [Type::CHR_CLASS, ' (Class)', $classes->getMatches(), []];
-
- if ($this->searchMask & SEARCH_TYPE_OPEN)
- foreach ($classes->iterate() as $id => $__)
- $osInfo[3][$id] = 'class_'.strToLower($classes->getField('fileString'));
-
- if ($classes->getMatches() > $this->maxResults)
- {
- // $result['note'] = sprintf(Util::$tryNarrowingString, 'LANG.lvnote_', $classes->getMatches(), $this->maxResults);
- $result['_truncated'] = 1;
- }
-
- return ['class', $result, null, $osInfo];
- }
-
- return false;
- }
-
- private function _searchCharRace($cndBase) // 1 Races: $searchMask & 0x00000002
- {
- $cnd = array_merge($cndBase, [$this->createLookup()]);
- $races = new CharRaceList($cnd);
-
- if ($data = $races->getListviewData())
- {
- $result['data'] = array_values($data);
- $osInfo = [Type::CHR_RACE, ' (Race)', $races->getMatches(), []];
-
- if ($this->searchMask & SEARCH_TYPE_OPEN)
- foreach ($races->iterate() as $id => $__)
- $osInfo[3][$id] = 'race_'.strToLower($races->getField('fileString')).'_male';
-
- if ($races->getMatches() > $this->maxResults)
- {
- // $result['note'] = sprintf(Util::$tryNarrowingString, 'LANG.lvnote_', $races->getMatches(), $this->maxResults);
- $result['_truncated'] = 1;
- }
-
- return ['race', $result, null, $osInfo];
- }
-
- return false;
- }
-
- private function _searchTitle($cndBase) // 2 Titles: $searchMask & 0x00000004
- {
- $cnd = array_merge($cndBase, [$this->createLookup(['male_loc'.User::$localeId, 'female_loc'.User::$localeId])]);
- $titles = new TitleList($cnd);
-
- if ($data = $titles->getListviewData())
- {
- $result['data'] = array_values($data);
- $osInfo = [Type::TITLE, ' (Title)', $titles->getMatches(), []];
-
- if ($this->searchMask & SEARCH_TYPE_OPEN)
- foreach ($titles->iterate() as $id => $__)
- $osInfo[3][$id] = $titles->getField('side');
-
- if ($titles->getMatches() > $this->maxResults)
- {
- // $result['note'] = sprintf(Util::$tryNarrowingString, 'LANG.lvnote_', $titles->getMatches(), $this->maxResults);
- $result['_truncated'] = 1;
- }
-
- return ['title', $result, null, $osInfo];
- }
-
- return false;
- }
-
- private function _searchWorldEvent($cndBase) // 3 World Events: $searchMask & 0x00000008
- {
- $cnd = array_merge($cndBase, array(
- array(
- 'OR',
- $this->createLookup(['h.name_loc'.User::$localeId]),
- ['AND', $this->createLookup(['e.description']), ['e.holidayId', 0]]
- )
- ));
- $wEvents = new WorldEventList($cnd);
-
- if ($data = $wEvents->getListviewData())
- {
- if ($this->searchMask & SEARCH_TYPE_REGULAR)
- $this->extendGlobalData($wEvents->getJSGlobals());
-
- $result['data'] = array_values($data);
- $osInfo = [Type::WORLDEVENT, ' (World Event)', $wEvents->getMatches()];
-
- // as allways: dates are updated in postCache-step
-
- if ($wEvents->getMatches() > $this->maxResults)
- {
- // $result['note'] = sprintf(Util::$tryNarrowingString, 'LANG.lvnote_', $wEvents->getMatches(), $this->maxResults);
- $result['_truncated'] = 1;
- }
-
- return ['event', $result, null, $osInfo];
- }
-
- return false;
- }
-
- private function _searchCurrency($cndBase) // 4 Currencies $searchMask & 0x0000010
- {
- $cnd = array_merge($cndBase, [$this->createLookup()]);
- $money = new CurrencyList($cnd);
-
- if ($data = $money->getListviewData())
- {
- $result['data'] = array_values($data);
- $osInfo = [Type::CURRENCY, ' (Currency)', $money->getMatches()];
-
- if ($this->searchMask & SEARCH_TYPE_OPEN)
- foreach ($money->iterate() as $id => $__)
- $osInfo[3][$id] = strToLower($money->getField('iconString'));
-
- if ($money->getMatches() > $this->maxResults)
- {
- $result['note'] = sprintf(Util::$tryNarrowingString, 'LANG.lvnote_currenciesfound', $money->getMatches(), $this->maxResults);
- $result['_truncated'] = 1;
- }
-
- return ['currency', $result, null, $osInfo];
- }
-
- return false;
- }
-
- private function _searchItemset($cndBase, &$shared) // 5 Itemsets $searchMask & 0x0000020
- {
- $cnd = array_merge($cndBase, [is_int($this->query) ? ['id', $this->query] : $this->createLookup()]);
- $sets = new ItemsetList($cnd);
-
- if ($data = $sets->getListviewData())
- {
- if ($this->searchMask & SEARCH_TYPE_REGULAR)
- $this->extendGlobalData($sets->getJSGlobals(GLOBALINFO_SELF));
-
- $result['data'] = array_values($data);
- $osInfo = [Type::ITEMSET, ' (Item Set)', $sets->getMatches()];
-
- if ($this->searchMask & SEARCH_TYPE_OPEN)
- foreach ($sets->iterate() as $id => $__)
- $osInfo[3][$id] = $sets->getField('quality');
-
- $shared['pcsToSet'] = $sets->pieceToSet;
-
- if ($sets->getMatches() > $this->maxResults)
- {
- $result['note'] = sprintf(Util::$tryNarrowingString, 'LANG.lvnote_itemsetsfound', $sets->getMatches(), $this->maxResults);
- $result['_truncated'] = 1;
- }
-
- if (isset($result['note']))
- $result['note'] .= ' + LANG.dash + $WH.sprintf(LANG.lvnote_filterresults, \'?itemsets&filter=na='.urlencode($this->search).'\')';
- else
- $result['note'] = '$$WH.sprintf(LANG.lvnote_filterresults, \'?itemsets&filter=na='.urlencode($this->search).'\')';
-
- return ['itemset', $result, null, $osInfo];
- }
-
- return false;
- }
-
- private function _searchItem($cndBase, &$shared) // 6 Items $searchMask & 0x0000040
- {
- $miscData = [];
- $cndAdd = empty($this->query) ? [] : (is_int($this->query) ? ['id', $this->query] : $this->createLookup());
-
- if (($this->searchMask & SEARCH_TYPE_JSON) && ($this->searchMask & 0x20) && !empty($shared['pcsToSet']))
- {
- $cnd = [['i.id', array_keys($shared['pcsToSet'])], CFG_SQL_LIMIT_NONE];
- $miscData = ['pcsToSet' => $shared['pcsToSet']];
- }
- else if (($this->searchMask & SEARCH_TYPE_JSON) && ($this->searchMask & 0x40))
- {
- $cnd = $cndBase;
- $cnd[] = ['i.class', [ITEM_CLASS_WEAPON, ITEM_CLASS_GEM, ITEM_CLASS_ARMOR]];
- $cnd[] = $cndAdd;
-
- if ($_ = array_filter($this->_get['slots'] ?? []))
- $cnd[] = ['slot', $_];
-
- // trick ItemListFilter into evaluating weights
- if ($this->statWeights)
- $_GET['filter'] = 'wt='.implode(':', $this->statWeights[0]).';wtv='.implode(':', $this->statWeights[1]);
-
- $itemFilter = new ItemListFilter();
- if ($_ = $itemFilter->createConditionsForWeights())
- {
- $miscData['extraOpts'] = $itemFilter->extraOpts;
- $cnd = array_merge($cnd, [$_]);
- }
- }
- else
- $cnd = array_merge($cndBase, [$cndAdd]);
-
- $items = new ItemList($cnd, $miscData);
-
- if ($data = $items->getListviewData($this->searchMask & SEARCH_TYPE_JSON ? (ITEMINFO_SUBITEMS | ITEMINFO_JSON) : 0))
- {
- if ($this->searchMask & SEARCH_TYPE_REGULAR)
- $this->extendGlobalData($items->getJSGlobals());
-
- foreach ($items->iterate() as $itemId => $__)
- if (!empty($data[$itemId]['subitems']))
- foreach ($data[$itemId]['subitems'] as &$si)
- $si['enchantment'] = implode(', ', $si['enchantment']);
-
- $osInfo = [Type::ITEM, ' (Item)', $items->getMatches(), [], []];
- $result['data'] = array_values($data);
-
- if ($this->searchMask & SEARCH_TYPE_OPEN)
- {
- foreach ($items->iterate() as $id => $__)
- {
- $osInfo[3][$id] = $items->getField('iconString');
- $osInfo[4][$id] = $items->getField('quality');
- }
- }
-
- if ($items->getMatches() > $this->maxResults)
- {
- $result['note'] = sprintf(Util::$tryNarrowingString, 'LANG.lvnote_itemsfound', $items->getMatches(), $this->maxResults);
- $result['_truncated'] = 1;
- }
-
- if (isset($result['note']))
- $result['note'] .= ' + LANG.dash + $WH.sprintf(LANG.lvnote_filterresults, \'?items&filter=na='.urlencode($this->search).'\')';
- else
- $result['note'] = '$$WH.sprintf(LANG.lvnote_filterresults, \'?items&filter=na='.urlencode($this->search).'\')';
-
- return ['item', $result, null, $osInfo];
- }
-
- return false;
- }
-
- private function _searchAbility($cndBase) // 7 Abilities (Player + Pet) $searchMask & 0x0000080
- {
- $cnd = array_merge($cndBase, array( // hmm, inclued classMounts..?
- ['s.typeCat', [7, -2, -3, -4]],
- [['s.cuFlags', (SPELL_CU_TRIGGERED | SPELL_CU_TALENT), '&'], 0],
- [['s.attributes0', 0x80, '&'], 0],
- $this->createLookup()
- ));
- $abilities = new SpellList($cnd);
-
- if ($data = $abilities->getListviewData())
- {
- if ($this->searchMask & SEARCH_TYPE_REGULAR)
- $this->extendGlobalData($abilities->getJSGlobals(GLOBALINFO_SELF | GLOBALINFO_RELATED));
-
- $multiClass = 0;
- foreach ($data as $d)
- {
- $multiClass = 0;
- for ($i = 1; $i <= 10; $i++)
- if ($d['reqclass'] & (1 << ($i - 1)))
- $multiClass++;
-
- if ($multiClass > 1)
- break;
- }
-
- $vis = ['level', 'schools'];
- if ($abilities->hasSetFields(['reagent1']))
- $vis[] = 'reagents';
-
- $vis[] = $multiClass > 1 ? 'classes' : 'singleclass';
-
- $osInfo = [Type::SPELL, ' (Ability)', $abilities->getMatches(), [], []];
- $result = array(
- 'data' => array_values($data),
- 'id' => 'abilities',
- 'name' => '$LANG.tab_abilities',
- 'visibleCols' => $vis
- );
-
- if ($this->searchMask & SEARCH_TYPE_OPEN)
- {
- foreach ($abilities->iterate() as $id => $__)
- {
- $osInfo[3][$id] = strToLower($abilities->getField('iconString'));
- $osInfo[4][$id] = $abilities->ranks[$id];
- }
- }
-
- if ($abilities->getMatches() > $this->maxResults)
- {
- $result['note'] = sprintf(Util::$tryNarrowingString, 'LANG.lvnote_abilitiesfound', $abilities->getMatches(), $this->maxResults);
- $result['_truncated'] = 1;
- }
-
- if (isset($result['note']))
- $result['note'] .= ' + LANG.dash + $WH.sprintf(LANG.lvnote_filterresults, \'?spells=7&filter=na='.urlencode($this->search).'\')';
- else
- $result['note'] = '$$WH.sprintf(LANG.lvnote_filterresults, \'?spells=7&filter=na='.urlencode($this->search).'\')';
-
- return ['spell', $result, null, $osInfo];
- }
-
- return false;
- }
-
- private function _searchTalent($cndBase) // 8 Talents (Player + Pet) $searchMask & 0x0000100
- {
- $cnd = array_merge($cndBase, array(
- ['s.typeCat', [-7, -2]],
- $this->createLookup()
- ));
- $talents = new SpellList($cnd);
-
- if ($data = $talents->getListviewData())
- {
- if ($this->searchMask & SEARCH_TYPE_REGULAR)
- $this->extendGlobalData($talents->getJSGlobals());
-
- $vis = ['level', 'singleclass', 'schools'];
- if ($talents->hasSetFields(['reagent1']))
- $vis[] = 'reagents';
-
- $osInfo = [Type::SPELL, ' (Talent)', $talents->getMatches(), [], []];
- $result = array(
- 'data' => array_values($data),
- 'id' => 'talents',
- 'name' => '$LANG.tab_talents',
- 'visibleCols' => $vis
- );
-
- if ($this->searchMask & SEARCH_TYPE_OPEN)
- {
- foreach ($talents->iterate() as $id => $__)
- {
- $osInfo[3][$id] = strToLower($talents->getField('iconString'));
- $osInfo[4][$id] = $talents->ranks[$talents->id];
- }
- }
-
- if ($talents->getMatches() > $this->maxResults)
- {
- $result['note'] = sprintf(Util::$tryNarrowingString, 'LANG.lvnote_talentsfound', $talents->getMatches(), $this->maxResults);
- $result['_truncated'] = 1;
- }
-
- if (isset($result['note']))
- $result['note'] .= ' + LANG.dash + $WH.sprintf(LANG.lvnote_filterresults, \'?spells=-2&filter=na='.urlencode($this->search).'\')';
- else
- $result['note'] = '$$WH.sprintf(LANG.lvnote_filterresults, \'?spells=-2&filter=na='.urlencode($this->search).'\')';
-
- return ['spell', $result, null, $osInfo];
- }
-
- return false;
- }
-
- private function _searchGlyph($cndBase) // 9 Glyphs $searchMask & 0x0000200
- {
- $cnd = array_merge($cndBase, array(
- ['s.typeCat', -13],
- $this->createLookup()
- ));
- $glyphs = new SpellList($cnd);
-
- if ($data = $glyphs->getListviewData())
- {
- if ($this->searchMask & SEARCH_TYPE_REGULAR)
- $this->extendGlobalData($glyphs->getJSGlobals(GLOBALINFO_SELF));
-
- $osInfo = [Type::SPELL, ' (Glyph)', $glyphs->getMatches(), []];
- $result = array(
- 'data' => array_values($data),
- 'id' => 'glyphs',
- 'name' => '$LANG.tab_glyphs',
- 'visibleCols' => ['singleclass', 'glyphtype']
- );
-
- if ($this->searchMask & SEARCH_TYPE_OPEN)
- foreach ($glyphs->iterate() as $id => $__)
- $osInfo[3][$id] = strToLower($glyphs->getField('iconString'));
-
- if ($glyphs->getMatches() > $this->maxResults)
- {
- $result['note'] = sprintf(Util::$tryNarrowingString, 'LANG.lvnote_glyphsfound', $glyphs->getMatches(), $this->maxResults);
- $result['_truncated'] = 1;
- }
-
- if (isset($result['note']))
- $result['note'] .= ' + LANG.dash + $WH.sprintf(LANG.lvnote_filterresults, \'?spells=-13&filter=na='.urlencode($this->search).'\')';
- else
- $result['note'] = '$$WH.sprintf(LANG.lvnote_filterresults, \'?spells=-13&filter=na='.urlencode($this->search).'\')';
-
- return ['spell', $result, null, $osInfo];
- }
-
- return false;
- }
-
- private function _searchProficiency($cndBase) // 10 Proficiencies $searchMask & 0x0000400
- {
- $cnd = array_merge($cndBase, array(
- ['s.typeCat', -11],
- $this->createLookup()
- ));
- $prof = new SpellList($cnd);
-
- if ($data = $prof->getListviewData())
- {
- if ($this->searchMask & SEARCH_TYPE_REGULAR)
- $this->extendGlobalData($prof->getJSGlobals(GLOBALINFO_SELF));
-
- $osInfo = [Type::SPELL, ' (Proficiency)', $prof->getMatches(), []];
- $result = array(
- 'data' => array_values($data),
- 'id' => 'proficiencies',
- 'name' => '$LANG.tab_proficiencies',
- 'visibleCols' => ['classes']
- );
-
- if ($this->searchMask & SEARCH_TYPE_OPEN)
- foreach ($prof->iterate() as $id => $__)
- $osInfo[3][$id] = strToLower($prof->getField('iconString'));
-
- if ($prof->getMatches() > $this->maxResults)
- {
- $result['note'] = sprintf(Util::$tryNarrowingString, 'LANG.lvnote_spellsfound', $prof->getMatches(), $this->maxResults);
- $result['_truncated'] = 1;
- }
-
- if (isset($result['note']))
- $result['note'] .= ' + LANG.dash + $WH.sprintf(LANG.lvnote_filterresults, \'?spells=-11&filter=na='.urlencode($this->search).'\')';
- else
- $result['note'] = '$$WH.sprintf(LANG.lvnote_filterresults, \'?spells=-11&filter=na='.urlencode($this->search).'\')';
-
- return ['spell', $result, null, $osInfo];
- }
-
- return false;
- }
-
- private function _searchProfession($cndBase) // 11 Professions (Primary + Secondary) $searchMask & 0x0000800
- {
- $cnd = array_merge($cndBase, array(
- ['s.typeCat', [9, 11]],
- $this->createLookup()
- ));
- $prof = new SpellList($cnd);
-
- if ($data = $prof->getListviewData())
- {
- if ($this->searchMask & SEARCH_TYPE_REGULAR)
- $this->extendGlobalData($prof->getJSGlobals(GLOBALINFO_SELF | GLOBALINFO_RELATED));
-
- $osInfo = [Type::SPELL, ' (Profession)', $prof->getMatches()];
- $result = array(
- 'data' => array_values($data),
- 'id' => 'professions',
- 'name' => '$LANG.tab_professions',
- 'visibleCols' => ['source', 'reagents']
- );
-
- if ($this->searchMask & SEARCH_TYPE_OPEN)
- foreach ($prof->iterate() as $id => $__)
- $osInfo[3][$id] = strToLower($prof->getField('iconString'));
-
- if ($prof->getMatches() > $this->maxResults)
- {
- $result['note'] = sprintf(Util::$tryNarrowingString, 'LANG.lvnote_professionfound', $prof->getMatches(), $this->maxResults);
- $result['_truncated'] = 1;
- }
-
- if (isset($result['note']))
- $result['note'] .= ' + LANG.dash + $WH.sprintf(LANG.lvnote_filterresults, \'?spells=11&filter=na='.urlencode($this->search).'\')';
- else
- $result['note'] = '$$WH.sprintf(LANG.lvnote_filterresults, \'?spells=11&filter=na='.urlencode($this->search).'\')';
-
- return ['spell', $result, null, $osInfo];
- }
-
- return false;
- }
-
- private function _searchCompanion($cndBase) // 12 Companions $searchMask & 0x0001000
- {
- $cnd = array_merge($cndBase, array(
- ['s.typeCat', -6],
- $this->createLookup()
- ));
- $vPets = new SpellList($cnd);
-
- if ($data = $vPets->getListviewData())
- {
- if ($this->searchMask & SEARCH_TYPE_REGULAR)
- $this->extendGlobalData($vPets->getJSGlobals());
-
- $osInfo = [Type::SPELL, ' (Companion)', $vPets->getMatches(), []];
- $result = array(
- 'data' => array_values($data),
- 'id' => 'companions',
- 'name' => '$LANG.tab_companions',
- 'visibleCols' => ['reagents']
- );
-
- if ($this->searchMask & SEARCH_TYPE_OPEN)
- foreach ($vPets->iterate() as $id => $__)
- $osInfo[3][$id] = strToLower($vPets->getField('iconString'));
-
- if ($vPets->getMatches() > $this->maxResults)
- {
- $result['note'] = sprintf(Util::$tryNarrowingString, 'LANG.lvnote_companionsfound', $vPets->getMatches(), $this->maxResults);
- $result['_truncated'] = 1;
- }
-
- if (isset($result['note']))
- $result['note'] .= ' + LANG.dash + $WH.sprintf(LANG.lvnote_filterresults, \'?spells=-6&filter=na='.urlencode($this->search).'\')';
- else
- $result['note'] = '$$WH.sprintf(LANG.lvnote_filterresults, \'?spells=-6&filter=na='.urlencode($this->search).'\')';
-
- return ['spell', $result, null, $osInfo];
- }
-
- return false;
- }
-
- private function _searchMount($cndBase) // 13 Mounts $searchMask & 0x0002000
- {
- $cnd = array_merge($cndBase, array(
- ['s.typeCat', -5],
- $this->createLookup()
- ));
- $mounts = new SpellList($cnd);
-
- if ($data = $mounts->getListviewData())
- {
- if ($this->searchMask & SEARCH_TYPE_REGULAR)
- $this->extendGlobalData($mounts->getJSGlobals(GLOBALINFO_SELF));
-
- $osInfo = [Type::SPELL, ' (Mount)', $mounts->getMatches(), []];
- $result = array(
- 'data' => array_values($data),
- 'id' => 'mounts',
- 'name' => '$LANG.tab_mounts',
- );
-
- if ($this->searchMask & SEARCH_TYPE_OPEN)
- foreach ($mounts->iterate() as $id => $__)
- $osInfo[3][$id] = strToLower($mounts->getField('iconString'));
-
- if ($mounts->getMatches() > $this->maxResults)
- {
- $result['note'] = sprintf(Util::$tryNarrowingString, 'LANG.lvnote_mountsfound', $mounts->getMatches(), $this->maxResults);
- $result['_truncated'] = 1;
- }
-
- if (isset($result['note']))
- $result['note'] .= ' + LANG.dash + $WH.sprintf(LANG.lvnote_filterresults, \'?spells=-5&filter=na='.urlencode($this->search).'\')';
- else
- $result['note'] = '$$WH.sprintf(LANG.lvnote_filterresults, \'?spells=-5&filter=na='.urlencode($this->search).'\')';
-
- return ['spell', $result, null, $osInfo];
- }
-
- return false;
- }
-
- private function _searchCreature($cndBase) // 14 NPCs $searchMask & 0x0004000
- {
- $cnd = array_merge($cndBase, array(
- [['flagsExtra', 0x80], 0], // exclude trigger creatures
- [['cuFlags', NPC_CU_DIFFICULTY_DUMMY, '&'], 0], // exclude difficulty entries
- $this->createLookup()
- ));
- $npcs = new CreatureList($cnd);
-
- if ($data = $npcs->getListviewData())
- {
- $osInfo = [Type::NPC, ' (NPC)', $npcs->getMatches()];
- $result = array(
- 'data' => array_values($data),
- 'id' => 'npcs',
- 'name' => '$LANG.tab_npcs',
- );
-
- if ($npcs->getMatches() > $this->maxResults)
- {
- $result['note'] = sprintf(Util::$tryNarrowingString, 'LANG.lvnote_npcsfound', $npcs->getMatches(), $this->maxResults);
- $result['_truncated'] = 1;
- }
-
- if (isset($result['note']))
- $result['note'] .= ' + LANG.dash + $WH.sprintf(LANG.lvnote_filterresults, \'?npcs&filter=na='.urlencode($this->search).'\')';
- else
- $result['note'] = '$$WH.sprintf(LANG.lvnote_filterresults, \'?npcs&filter=na='.urlencode($this->search).'\')';
-
- return ['creature', $result, null, $osInfo];
- }
-
- return false;
- }
-
- private function _searchQuest($cndBase) // 15 Quests $searchMask & 0x0008000
- {
- $cnd = array_merge($cndBase, array(
- [['flags', CUSTOM_UNAVAILABLE | CUSTOM_DISABLED, '&'], 0],
- $this->createLookup()
- ));
- $quests = new QuestList($cnd);
-
- if ($data = $quests->getListviewData())
- {
- $osInfo = [Type::QUEST, ' (Quest)', $quests->getMatches()];
- $result['data'] = array_values($data);
-
- if ($this->searchMask & SEARCH_TYPE_REGULAR)
- $this->extendGlobalData($quests->getJSGlobals());
-
- if ($quests->getMatches() > $this->maxResults)
- {
- $result['note'] = sprintf(Util::$tryNarrowingString, 'LANG.lvnote_questsfound', $quests->getMatches(), $this->maxResults);
- $result['_truncated'] = 1;
- }
-
- if (isset($result['note']))
- $result['note'] .= ' + LANG.dash + $WH.sprintf(LANG.lvnote_filterresults, \'?quests&filter=na='.urlencode($this->search).'\')';
- else
- $result['note'] = '$$WH.sprintf(LANG.lvnote_filterresults, \'?quests&filter=na='.urlencode($this->search).'\')';
-
- return ['quest', $result, null, $osInfo];
- }
-
- return false;
- }
-
- private function _searchAchievement($cndBase) // 16 Achievements $searchMask & 0x0010000
- {
- $cnd = array_merge($cndBase, array(
- [['flags', ACHIEVEMENT_FLAG_COUNTER, '&'], 0], // not a statistic
- $this->createLookup()
- ));
- $acvs = new AchievementList($cnd);
-
- if ($data = $acvs->getListviewData())
- {
- if ($this->searchMask & SEARCH_TYPE_REGULAR)
- $this->extendGlobalData($acvs->getJSGlobals());
-
- $osInfo = [Type::ACHIEVEMENT, ' (Achievement)', $acvs->getMatches(), []];
- $result = array(
- 'data' => array_values($data),
- 'visibleCols' => ['category']
- );
-
- if ($this->searchMask & SEARCH_TYPE_OPEN)
- foreach ($acvs->iterate() as $id => $__)
- $osInfo[3][$id] = strToLower($acvs->getField('iconString'));
-
- if ($acvs->getMatches() > $this->maxResults)
- {
- $result['note'] = sprintf(Util::$tryNarrowingString, 'LANG.lvnote_achievementsfound', $acvs->getMatches(), $this->maxResults);
- $result['_truncated'] = 1;
- }
-
- if (isset($result['note']))
- $result['note'] .= ' + LANG.dash + $WH.sprintf(LANG.lvnote_filterresults, \'?achieveemnts&filter=na='.urlencode($this->search).'\')';
- else
- $result['note'] = '$$WH.sprintf(LANG.lvnote_filterresults, \'?achievements&filter=na='.urlencode($this->search).'\')';
-
- return ['achievement', $result, null, $osInfo];
- }
-
- return false;
- }
-
- private function _searchStatistic($cndBase) // 17 Statistics $searchMask & 0x0020000
- {
- $cnd = array_merge($cndBase, array(
- ['flags', ACHIEVEMENT_FLAG_COUNTER, '&'], // is a statistic
- $this->createLookup()
- ));
- $stats = new AchievementList($cnd);
-
- if ($data = $stats->getListviewData())
- {
- if ($this->searchMask & SEARCH_TYPE_REGULAR)
- $this->extendGlobalData($stats->getJSGlobals(GLOBALINFO_SELF));
-
- $osInfo = [Type::ACHIEVEMENT, ' (Statistic)', $stats->getMatches()];
- $result = array(
- 'data' => array_values($data),
- 'visibleCols' => ['category'],
- 'hiddenCols' => ['side', 'points', 'rewards'],
- 'name' => '$LANG.tab_statistics',
- 'id' => 'statistics'
- );
-
- if ($stats->getMatches() > $this->maxResults)
- {
- $result['note'] = sprintf(Util::$tryNarrowingString, 'LANG.lvnote_statisticsfound', $stats->getMatches(), $this->maxResults);
- $result['_truncated'] = 1;
- }
-
- if (isset($result['note']))
- $result['note'] .= ' + LANG.dash + $WH.sprintf(LANG.lvnote_filterresults, \'?achievements=1&filter=na='.urlencode($this->search).'\')';
- else
- $result['note'] = '$$WH.sprintf(LANG.lvnote_filterresults, \'?achievements=1&filter=na='.urlencode($this->search).'\')';
-
- return ['achievement', $result, null, $osInfo];
- }
-
- return false;
- }
-
- private function _searchZone($cndBase) // 18 Zones $searchMask & 0x0040000
- {
- $cnd = array_merge($cndBase, [$this->createLookup()]);
- $zones = new ZoneList($cnd);
-
- if ($data = $zones->getListviewData())
- {
- if ($this->searchMask & SEARCH_TYPE_REGULAR)
- $this->extendGlobalData($zones->getJSGlobals());
-
- $osInfo = [Type::ZONE, ' (Zone)', $zones->getMatches()];
- $result['data'] = array_values($data);
-
- if ($zones->getMatches() > $this->maxResults)
- {
- $result['note'] = sprintf(Util::$tryNarrowingString, 'LANG.lvnote_zonesfound', $zones->getMatches(), $this->maxResults);
- $result['_truncated'] = 1;
- }
-
- return ['zone', $result, null, $osInfo];
- }
-
- return false;
- }
-
- private function _searchObject($cndBase) // 19 Objects $searchMask & 0x0080000
- {
- $cnd = array_merge($cndBase, [$this->createLookup()]);
- $objects = new GameObjectList($cnd);
-
- if ($data = $objects->getListviewData())
- {
- if ($this->searchMask & SEARCH_TYPE_REGULAR)
- $this->extendGlobalData($objects->getJSGlobals());
-
- $osInfo = [Type::OBJECT, ' (Object)', $objects->getMatches()];
- $result['data'] = array_values($data);
-
- if ($objects->getMatches() > $this->maxResults)
- {
- $result['note'] = sprintf(Util::$tryNarrowingString, 'LANG.lvnote_objectsfound', $objects->getMatches(), $this->maxResults);
- $result['_truncated'] = 1;
- }
-
- if (isset($result['note']))
- $result['note'] .= ' + LANG.dash + $WH.sprintf(LANG.lvnote_filterresults, \'?objects&filter=na='.urlencode($this->search).'\')';
- else
- $result['note'] = '$$WH.sprintf(LANG.lvnote_filterresults, \'?objects&filter=na='.urlencode($this->search).'\')';
-
- return ['object', $result, null, $osInfo];
- }
-
- return false;
- }
-
- private function _searchFaction($cndBase) // 20 Factions $searchMask & 0x0100000
- {
- $cnd = array_merge($cndBase, [$this->createLookup()]);
- $factions = new FactionList($cnd);
-
- if ($data = $factions->getListviewData())
- {
- $osInfo = [Type::FACTION, ' (Faction)', $factions->getMatches()];
- $result['data'] = array_values($data);
-
- if ($factions->getMatches() > $this->maxResults)
- {
- $result['note'] = sprintf(Util::$tryNarrowingString, 'LANG.lvnote_factionsfound', $factions->getMatches(), $this->maxResults);
- $result['_truncated'] = 1;
- }
-
- return ['faction', $result, null, $osInfo];
- }
-
- return false;
- }
-
- private function _searchSkill($cndBase) // 21 Skills $searchMask & 0x0200000
- {
- $cnd = array_merge($cndBase, [$this->createLookup()]);
- $skills = new SkillList($cnd);
-
- if ($data = $skills->getListviewData())
- {
- $osInfo = [Type::SKILL, ' (Skill)', $skills->getMatches(), []];
- $result['data'] = array_values($data);
-
- if ($this->searchMask & SEARCH_TYPE_OPEN)
- foreach ($skills->iterate() as $id => $__)
- $osInfo[3][$id] = $skills->getField('iconString');
-
- if ($skills->getMatches() > $this->maxResults)
- {
- $result['note'] = sprintf(Util::$tryNarrowingString, 'LANG.lvnote_skillsfound', $skills->getMatches(), $this->maxResults);
- $result['_truncated'] = 1;
- }
-
- return ['skill', $result, null, $osInfo];
- }
-
- return false;
- }
-
- private function _searchPet($cndBase) // 22 Pets $searchMask & 0x0400000
- {
- $cnd = array_merge($cndBase, [$this->createLookup()]);
- $pets = new PetList($cnd);
-
- if ($data = $pets->getListviewData())
- {
- $osInfo = [Type::PET, ' (Pet)', $pets->getMatches(), []];
- $result = array(
- 'data' => array_values($data),
- 'computeDataFunc' => '$_'
- );
-
- if ($this->searchMask & SEARCH_TYPE_OPEN)
- foreach ($pets->iterate() as $id => $__)
- $osInfo[3][$id] = $pets->getField('iconString');
-
- if ($pets->getMatches() > $this->maxResults)
- {
- $result['note'] = sprintf(Util::$tryNarrowingString, 'LANG.lvnote_petsfound', $pets->getMatches(), $this->maxResults);
- $result['_truncated'] = 1;
- }
-
- return ['pet', $result, 'petFoodCol', $osInfo];
- }
-
- return false;
- }
-
- private function _searchCreatureAbility($cndBase) // 23 NPCAbilities $searchMask & 0x0800000
- {
- $cnd = array_merge($cndBase, array(
- ['s.typeCat', -8],
- $this->createLookup()
- ));
- $npcAbilities = new SpellList($cnd);
-
- if ($data = $npcAbilities->getListviewData())
- {
- if ($this->searchMask & SEARCH_TYPE_REGULAR)
- $this->extendGlobalData($npcAbilities->getJSGlobals(GLOBALINFO_SELF));
-
- $osInfo = [Type::SPELL, ' (Spell)', $npcAbilities->getMatches(), []];
- $result = array(
- 'data' => array_values($data),
- 'id' => 'npc-abilities',
- 'name' => '$LANG.tab_npcabilities',
- 'visibleCols' => ['level'],
- 'hiddenCols' => ['skill']
- );
-
- if ($this->searchMask & SEARCH_TYPE_OPEN)
- foreach ($npcAbilities->iterate() as $id => $__)
- $osInfo[3][$id] = strToLower($npcAbilities->getField('iconString'));
-
- if ($npcAbilities->getMatches() > $this->maxResults)
- {
- $result['note'] = sprintf(Util::$tryNarrowingString, 'LANG.lvnote_spellsfound', $npcAbilities->getMatches(), $this->maxResults);
- $result['_truncated'] = 1;
- }
-
- if (isset($result['note']))
- $result['note'] .= ' + LANG.dash + $WH.sprintf(LANG.lvnote_filterresults, \'?spells=-8&filter=na='.urlencode($this->search).'\')';
- else
- $result['note'] = '$$WH.sprintf(LANG.lvnote_filterresults, \'?spells=-8&filter=na='.urlencode($this->search).'\')';
-
- return ['spell', $result, null, $osInfo];
- }
-
- return false;
- }
-
- private function _searchSpell($cndBase) // 24 Spells (Misc + GM + triggered abilities) $searchMask & 0x1000000
- {
- $cnd = array_merge($cndBase, array(
- ['s.typeCat', -8, '!'],
- [
- 'OR',
- ['s.typeCat', [0, -9]],
- ['s.cuFlags', SPELL_CU_TRIGGERED, '&'],
- ['s.attributes0', 0x80, '&']
- ],
- $this->createLookup()
- ));
- $misc = new SpellList($cnd);
-
- if ($data = $misc->getListviewData())
- {
- if ($this->searchMask & SEARCH_TYPE_REGULAR)
- $this->extendGlobalData($misc->getJSGlobals(GLOBALINFO_SELF | GLOBALINFO_RELATED));
-
- $osInfo = [Type::SPELL, ' (Spell)', $misc->getMatches(), []];
- $result = array(
- 'data' => array_values($data),
- 'name' => '$LANG.tab_uncategorizedspells',
- 'visibleCols' => ['level'],
- 'hiddenCols' => ['skill']
- );
-
- if ($this->searchMask & SEARCH_TYPE_OPEN)
- foreach ($misc->iterate() as $id => $__)
- $osInfo[3][$id] = strToLower($misc->getField('iconString'));
-
- if ($misc->getMatches() > $this->maxResults)
- {
- $result['note'] = sprintf(Util::$tryNarrowingString, 'LANG.lvnote_spellsfound', $misc->getMatches(), $this->maxResults);
- $result['_truncated'] = 1;
- }
-
- if (isset($result['note']))
- $result['note'] .= ' + LANG.dash + $WH.sprintf(LANG.lvnote_filterresults, \'?spells=0&filter=na='.urlencode($this->search).'\')';
- else
- $result['note'] = '$$WH.sprintf(LANG.lvnote_filterresults, \'?spells=0&filter=na='.urlencode($this->search).'\')';
-
- return ['spell', $result, null, $osInfo];
- }
-
- return false;
- }
-
- private function _searchEmote($cndBase) // 25 Emotes $searchMask & 0x2000000
- {
- $cnd = array_merge($cndBase, [$this->createLookup(['cmd', 'self_loc'.User::$localeId, 'target_loc'.User::$localeId, 'noTarget_loc'.User::$localeId])]);
- $emote = new EmoteList($cnd);
-
- if ($data = $emote->getListviewData())
- {
- $osInfo = [Type::EMOTE, ' (Emote)', $emote->getMatches()];
- $result = array(
- 'data' => array_values($data),
- 'name' => Util::ucFirst(Lang::game('emotes'))
- );
-
- return ['emote', $result, 'emote', $osInfo];
- }
-
- return false;
- }
-
- private function _searchEnchantment($cndBase) // 26 Enchantments $searchMask & 0x4000000
- {
- $cnd = array_merge($cndBase, [$this->createLookup(['name_loc'.User::$localeId])]);
- $enchantment = new EnchantmentList($cnd);
-
- if ($data = $enchantment->getListviewData())
- {
- $this->extendGlobalData($enchantment->getJSGlobals());
-
- $osInfo = [Type::ENCHANTMENT, ' (Enchantment)', $enchantment->getMatches()];
- $result = array(
- 'data' => array_values($data),
- 'name' => Util::ucFirst(Lang::game('enchantments'))
- );
-
- if (array_filter(array_column($result['data'], 'spells')))
- $result['visibleCols'] = ['trigger'];
-
- if (!$enchantment->hasSetFields(['skillLine']))
- $result['hiddenCols'] = ['skill'];
-
- if ($enchantment->getMatches() > $this->maxResults)
- {
- $result['note'] = sprintf(Util::$tryNarrowingString, 'LANG.lvnote_enchantmentsfound', $enchantment->getMatches(), $this->maxResults);
- $result['_truncated'] = 1;
- }
-
- return ['enchantment', $result, 'enchantment', $osInfo];
- }
-
- return false;
- }
-
- private function _searchSound($cndBase) // 27 Sounds $searchMask & 0x8000000
- {
- $cnd = array_merge($cndBase, [$this->createLookup(['name'])]);
- $sounds = new SoundList($cnd);
-
- if ($data = $sounds->getListviewData())
- {
- if ($this->searchMask & SEARCH_TYPE_REGULAR)
- $this->extendGlobalData($sounds->getJSGlobals());
-
- $osInfo = [Type::SOUND, ' (Sound)', $sounds->getMatches()];
- $result['data'] = array_values($data);
-
- if ($sounds->getMatches() > $this->maxResults)
- {
- $result['note'] = sprintf(Util::$tryNarrowingString, 'LANG.lvnote_soundsfound', $sounds->getMatches(), $this->maxResults);
- $result['_truncated'] = 1;
- }
-
- return ['sound', $result, null, $osInfo];
- }
-
- return false;
- }
-}
-
-?>
diff --git a/pages/skill.php b/pages/skill.php
deleted file mode 100644
index 92b2dcc2..00000000
--- a/pages/skill.php
+++ /dev/null
@@ -1,343 +0,0 @@
-typeId = intVal($id);
-
- $this->subject = new SkillList(array(['id', $this->typeId]));
- if ($this->subject->error)
- $this->notFound(Lang::game('skill'), Lang::skill('notFound'));
-
- $this->name = $this->subject->getField('name', true);
- $this->cat = $this->subject->getField('typeCat');
- }
-
- protected function generatePath()
- {
- $this->path[] = (in_array($this->cat, [9, 11]) || $this->typeId == 762) ? $this->typeId : $this->cat;
- }
-
- protected function generateTitle()
- {
- array_unshift($this->title, $this->name, Util::ucFirst(Lang::game('skill')));
- }
-
- protected function generateContent()
- {
- /***********/
- /* Infobox */
- /**********/
-
- $infobox = Lang::getInfoBoxForFlags(intval($this->subject->getField('cuFlags')));
-
- // icon
- if ($_ = $this->subject->getField('iconId'))
- {
- $infobox[] = Util::ucFirst(lang::game('icon')).Lang::main('colon').'[icondb='.$_.' name=true]';
- $this->extendGlobalIds(Type::ICON, $_);
- }
-
- /****************/
- /* Main Content */
- /****************/
-
- $this->infobox = $infobox ? '[ul][li]'.implode('[/li][li]', $infobox).'[/li][/ul]' : null;
- $this->headIcons = [$this->subject->getField('iconString')];
- $this->redButtons = array(
- BUTTON_WOWHEAD => true,
- BUTTON_LINKS => ['type' => $this->type, 'typeId' => $this->typeId]
- );
-
- if ($_ = $this->subject->getField('description', true))
- $this->extraText = $_;
-
- /**************/
- /* Extra Tabs */
- /**************/
-
- if (in_array($this->cat, [-5, 9, 11]))
- {
- // tab: recipes [spells] (crafted)
- $condition = array(
- ['OR', ['s.reagent1', 0, '>'], ['s.reagent2', 0, '>'], ['s.reagent3', 0, '>'], ['s.reagent4', 0, '>'], ['s.reagent5', 0, '>'], ['s.reagent6', 0, '>'], ['s.reagent7', 0, '>'], ['s.reagent8', 0, '>']],
- ['OR', ['s.skillLine1', $this->typeId], ['AND', ['s.skillLine1', 0, '>'], ['s.skillLine2OrMask', $this->typeId]]],
- CFG_SQL_LIMIT_NONE
- );
-
- $recipes = new SpellList($condition); // also relevant for 3
- if (!$recipes->error)
- {
- $this->extendGlobalData($recipes->getJSGlobals(GLOBALINFO_SELF | GLOBALINFO_RELATED));
-
- $this->lvTabs[] = ['spell', array(
- 'data' => array_values($recipes->getListviewData()),
- 'id' => 'recipes',
- 'name' => '$LANG.tab_recipes',
- 'visibleCols' => ['reagents', 'source'],
- 'note' => sprintf(Util::$filterResultString, '?spells='.$this->cat.'.'.$this->typeId.'&filter=cr=20;crs=1;crv=0')
- )];
- }
-
- // tab: recipe Items [items] (Books)
- $filterRecipe = [null, 165, 197, 202, 164, 185, 171, 129, 333, 356, 755, 773, 186, 182];
- $conditions = array(
- ['requiredSkill', $this->typeId],
- ['class', ITEM_CLASS_RECIPE],
- CFG_SQL_LIMIT_NONE
- );
-
- $recipeItems = new ItemList($conditions);
- if (!$recipeItems->error)
- {
- $this->extendGlobalData($recipeItems->getJSGlobals(GLOBALINFO_SELF));
-
- $tabData = array(
- 'data' => array_values($recipeItems->getListviewData()),
- 'id' => 'recipe-items',
- 'name' => '$LANG.tab_recipeitems',
- );
-
- if ($_ = array_search($this->typeId, $filterRecipe))
- $tabData['note'] = sprintf(Util::$filterResultString, "?items=9.".$_);
-
- $this->lvTabs[] = ['item', $tabData];
- }
-
- // tab: crafted items [items]
- $filterItem = [null, 171, 164, 185, 333, 202, 129, 755, 165, 186, 197, null, null, 356, 182, 773];
- $created = [];
- foreach ($recipes->iterate() as $__)
- if ($idx = $recipes->canCreateItem())
- foreach ($idx as $i)
- $created[] = $recipes->getField('effect'.$i.'CreateItemId');
-
- if ($created)
- {
- $created = new ItemList(array(['i.id', $created], CFG_SQL_LIMIT_NONE));
- if (!$created->error)
- {
- $this->extendGlobalData($created->getJSGlobals(GLOBALINFO_SELF));
-
- $tabData = array(
- 'data' => array_values($created->getListviewData()),
- 'id' => 'crafted-items',
- 'name' => '$LANG.tab_crafteditems',
- );
-
- if ($_ = array_search($this->typeId, $filterItem))
- $tabData['note'] = sprintf(Util::$filterResultString, "?items&filter=cr=86;crs=".$_.";crv=0");
-
- $this->lvTabs[] = ['item', $tabData];
- }
- }
-
- // tab: required by [item]
- $conditions = array(
- ['requiredSkill', $this->typeId],
- ['class', ITEM_CLASS_RECIPE, '!'],
- CFG_SQL_LIMIT_NONE
- );
-
- $reqBy = new ItemList($conditions);
- if (!$reqBy->error)
- {
- $this->extendGlobalData($reqBy->getJSGlobals(GLOBALINFO_SELF));
-
- $tabData = array(
- 'data' => array_values($reqBy->getListviewData()),
- 'id' => 'required-by',
- 'name' => '$LANG.tab_requiredby',
- );
-
- if ($_ = array_search($this->typeId, $filterItem))
- $tabData['note'] = sprintf(Util::$filterResultString, "?items&filter=99:168;crs=".$_.":2;crv=0:0");
-
- $this->lvTabs[] = ['item', $tabData];
- }
-
- // tab: required by [itemset]
- $conditions = array(
- ['skillId', $this->typeId],
- CFG_SQL_LIMIT_NONE
- );
-
- $reqBy = new ItemsetList($conditions);
- if (!$reqBy->error)
- {
- $this->extendGlobalData($reqBy->getJSGlobals(GLOBALINFO_SELF));
-
- $this->lvTabs[] = ['itemset', array(
- 'data' => array_values($reqBy->getListviewData()),
- 'id' => 'required-by-set',
- 'name' => '$LANG.tab_requiredby'
- )];
- }
- }
-
- // tab: spells [spells] (exclude first tab)
- $reqClass = 0x0;
- $reqRace = 0x0;
- $condition = array(
- ['AND', ['s.reagent1', 0], ['s.reagent2', 0], ['s.reagent3', 0], ['s.reagent4', 0], ['s.reagent5', 0], ['s.reagent6', 0], ['s.reagent7', 0], ['s.reagent8', 0]],
- ['OR', ['s.skillLine1', $this->typeId], ['AND', ['s.skillLine1', 0, '>'], ['s.skillLine2OrMask', $this->typeId]]],
- CFG_SQL_LIMIT_NONE
- );
-
- foreach (Game::$skillLineMask as $line1 => $sets)
- foreach ($sets as $idx => $set)
- if ($set[1] == $this->typeId)
- {
- $condition[1][] = array('AND', ['s.skillLine1', $line1], ['s.skillLine2OrMask', 1 << $idx, '&']);
- break 2;
- }
-
- $spells = new SpellList($condition);
- if (!$spells->error)
- {
- foreach ($spells->iterate() as $__)
- {
- $reqClass |= $spells->getField('reqClassMask');
- $reqRace |= $spells->getField('reqRaceMask');
- }
-
- $this->extendGlobalData($spells->getJSGlobals(GLOBALINFO_SELF));
-
- $tabData = array(
- 'data' => array_values($spells->getListviewData()),
- 'visibleCols' => ['source']
- );
-
- switch ($this->cat)
- {
- case -4:
- $tabData['note'] = sprintf(Util::$filterResultString, '?spells=-4');
- break;
- case 7:
- if ($this->typeId != 769) // Internal
- $tabData['note'] = sprintf(Util::$filterResultString, '?spells='.$this->cat.'.'.(log($reqClass, 2) + 1).'.'.$this->typeId); // doesn't matter what spell; reqClass should be identical for all Class Spells
- break;
- case 9:
- case 11:
- $tabData['note'] = sprintf(Util::$filterResultString, '?spells='.$this->cat.'.'.$this->typeId);
- break;
- }
-
- $this->lvTabs[] = ['spell', $tabData];
- }
-
- // tab: trainers [npcs]
- if (in_array($this->cat, [-5, 6, 7, 8, 9, 11]))
- {
- $mask = 0;
- foreach (Game::$skillLineMask[-3] as $idx => $pair)
- if ($pair[1] == $this->typeId)
- $mask |= 1 << $idx;
-
- $spellIds = DB::Aowow()->selectCol(
- 'SELECT id FROM ?_spell WHERE (skillLine1 = ?d OR (skillLine1 > 0 AND skillLine2OrMask = ?d) {OR (skillLine1 = -3 AND skillLine2OrMask = ?d)})',
- $this->typeId,
- $this->typeId,
- $mask ?: DBSIMPLE_SKIP
- );
-
- $list = $spellIds ? DB::World()->selectCol('SELECT cdt.CreatureId FROM creature_default_trainer cdt JOIN trainer_spell ts ON ts.TrainerId = cdt.TrainerId WHERE ts.SpellID IN (?a)', $spellIds) : [];
- if ($list)
- {
- $this->addScript([JS_FILE, '?data=zones&locale='.User::$localeId.'&t='.$_SESSION['dataKey']]);
-
- $trainer = new CreatureList(array(CFG_SQL_LIMIT_NONE, ['ct.id', $list], ['s.guid', NULL, '!'], ['ct.npcflag', 0x10, '&']));
-
- if (!$trainer->error)
- {
- $this->extendGlobalData($trainer->getJSGlobals());
-
- $this->lvTabs[] = ['creature', array(
- 'data' => array_values($trainer->getListviewData()),
- 'id' => 'trainer',
- 'name' => '$LANG.tab_trainers',
- )];
- }
- }
- }
-
- // tab: quests [quests]
- if (in_array($this->cat, [9, 11])) // only for professions
- {
- $sort = 0;
- switch ($this->typeId)
- {
- case 182: $sort = 24; break; // Herbalism
- case 356: $sort = 101; break; // Fishing
- case 164: $sort = 121; break; // Blacksmithing
- case 171: $sort = 181; break; // Alchemy
- case 165: $sort = 182; break; // Leatherworking
- case 202: $sort = 201; break; // Engineering
- case 197: $sort = 264; break; // Tailoring
- case 185: $sort = 304; break; // Cooking
- case 129: $sort = 324; break; // First Aid
- case 773: $sort = 371; break; // Inscription
- case 755: $sort = 373; break; // Jewelcrafting
- }
-
- if ($sort)
- {
- $quests = new QuestList(array(['zoneOrSort', -$sort], CFG_SQL_LIMIT_NONE));
- if (!$quests->error)
- {
- $this->extendGlobalData($quests->getJSGlobals());
- $this->lvTabs[] = ['quest', ['data' => array_values($quests->getListviewData())]];
- }
- }
- }
-
- // tab: related classes (apply classes from [spells])
- $class = [];
- for ($i = 0; $i < 11; $i++)
- if ($reqClass & (1 << $i))
- $class[] = $i + 1;
-
- if ($class)
- {
- $classes = new CharClassList(array(['id', $class]));
- if (!$classes->error)
- $this->lvTabs[] = ['class', ['data' => array_values($classes->getListviewData())]];
- }
-
- // tab: related races (apply races from [spells])
- $race = [];
- for ($i = 0; $i < 12; $i++)
- if ($reqRace & (1 << $i))
- $race[] = $i + 1;
-
- if ($race)
- {
- $races = new CharRaceList(array(['id', $race]));
- if (!$races->error)
- $this->lvTabs[] = ['race', ['data' => array_values($races->getListviewData())]];
- }
- }
-}
-
-
-?>
diff --git a/pages/skills.php b/pages/skills.php
deleted file mode 100644
index d423a9f9..00000000
--- a/pages/skills.php
+++ /dev/null
@@ -1,57 +0,0 @@
-getCategoryFromUrl($pageParam);;
-
- parent::__construct($pageCall, $pageParam);
-
- $this->name = Util::ucFirst(Lang::game('skills'));
- }
-
- protected function generateContent()
- {
- $conditions = [];
-
- if (!User::isInGroup(U_GROUP_EMPLOYEE))
- $conditions[] = [['cuFlags', CUSTOM_EXCLUDE_FOR_LISTVIEW, '&'], 0];
-
- if ($this->category)
- $conditions[] = ['typeCat', $this->category[0]];
-
- $skills = new SkillList($conditions);
-
- $this->lvTabs[] = ['skill', ['data' => array_values($skills->getListviewData())]];
- }
-
- protected function generateTitle()
- {
- if ($this->category)
- array_unshift($this->title, Lang::skill('cat', $this->category[0]));
- }
-
- protected function generatePath()
- {
- if ($this->category)
- $this->path[] = $this->category[0];
- }
-}
-
-?>
diff --git a/pages/sound.php b/pages/sound.php
deleted file mode 100644
index fb476401..00000000
--- a/pages/sound.php
+++ /dev/null
@@ -1,344 +0,0 @@
- ['filter' => FILTER_CALLBACK, 'options' => 'GenericPage::checkEmptySet']];
-
- private $cat = 0;
-
- public function __construct($pageCall, $id)
- {
- parent::__construct($pageCall, $id);
-
- // special case
- if (!$id && $this->_get['playlist'])
- {
- $this->special = true;
- $this->name = Lang::sound('cat', 1000);
- $this->cat = 1000;
- $this->articleUrl = 'sound&playlist';
- $this->contribute = CONTRIBUTE_NONE;
- $this->mode = CACHE_TYPE_NONE;
- }
- // regular case
- else
- {
- $this->typeId = intVal($id);
-
- $this->subject = new SoundList(array(['id', $this->typeId]));
- if ($this->subject->error)
- $this->notFound(Lang::game('sound'), Lang::sound('notFound'));
-
- $this->name = $this->subject->getField('name');
- $this->cat = $this->subject->getField('cat');
- }
- }
-
- protected function generatePath()
- {
- $this->path[] = $this->cat;
- }
-
- protected function generateTitle()
- {
- array_unshift($this->title, $this->name, Util::ucFirst(Lang::game('sound')));
- }
-
- protected function generateContent()
- {
- if ($this->special)
- $this->generatePlaylistContent();
- else
- $this->generateDefaultContent();
- }
-
- private function generatePlaylistContent()
- {
-
- }
-
- private function generateDefaultContent()
- {
- /****************/
- /* Main Content */
- /****************/
-
- $this->addScript([JS_FILE, '?data=zones&locale='.User::$localeId.'&t='.$_SESSION['dataKey']]);
-
- // get spawns
- $map = null;
- if ($spawns = $this->subject->getSpawns(SPAWNINFO_FULL))
- {
- $map = ['data' => ['parent' => 'mapper-generic'], 'mapperData' => &$spawns];
- foreach ($spawns as $areaId => &$areaData)
- $map['extra'][$areaId] = ZoneList::getName($areaId);
- }
-
- // get full path ingame for sound (workaround for missing PlaySoundKit())
- $fullpath = DB::Aowow()->selectCell('SELECT IF(sf.`path` <> "", CONCAT(sf.`path`, "\\\\", sf.`file`), sf.`file`) FROM ?_sounds_files sf JOIN ?_sounds s ON s.soundFile1 = sf.id WHERE s.id = ?d', $this->typeId);
-
- $this->map = $map;
- $this->headIcons = [$this->subject->getField('iconString')];
- $this->redButtons = array(
- BUTTON_WOWHEAD => true,
- BUTTON_PLAYLIST => true,
- BUTTON_LINKS => array(
- 'type' => Type::SOUND,
- 'typeId' => $this->typeId,
- 'sound' => str_replace('\\', '\\\\', $fullpath) // escape for wow client
- )
- );
-
- $this->extendGlobalData($this->subject->getJSGlobals());
-
-
- /**************/
- /* Extra Tabs */
- /**************/
-
- // tab: Spells
- // skipping (always empty): ready, castertargeting, casterstate, targetstate
- $displayIds = DB::Aowow()->selectCol('
- SELECT id FROM ?_spell_sounds WHERE
- animation = ?d OR
- precast = ?d OR
- cast = ?d OR
- impact = ?d OR
- state = ?d OR
- statedone = ?d OR
- channel = ?d OR
- casterimpact = ?d OR
- targetimpact = ?d OR
- missiletargeting = ?d OR
- instantarea = ?d OR
- persistentarea = ?d OR
- missile = ?d OR
- impactarea = ?d
- ', $this->typeId, $this->typeId, $this->typeId, $this->typeId, $this->typeId, $this->typeId, $this->typeId, $this->typeId, $this->typeId, $this->typeId, $this->typeId, $this->typeId, $this->typeId, $this->typeId);
-
- $cnd = array(
- 'OR',
- ['AND', ['effect1Id', 132], ['effect1MiscValue', $this->typeId]],
- ['AND', ['effect2Id', 132], ['effect2MiscValue', $this->typeId]],
- ['AND', ['effect3Id', 132], ['effect3MiscValue', $this->typeId]]
- );
-
- if ($displayIds)
- $cnd[] = ['spellVisualId', $displayIds];
-
- $spells = new SpellList($cnd);
- if (!$spells->error)
- {
- $data = $spells->getListviewData();
- $this->extendGlobalData($spells->getJSGlobals(GLOBALINFO_SELF));
-
- $this->lvTabs[] = ['spell', array(
- 'data' => array_values($data),
- )];
- }
-
-
- // tab: Items
- $subClasses = [];
- if ($subClassMask = DB::Aowow()->selectCell('SELECT subClassMask FROM ?_items_sounds WHERE soundId = ?d', $this->typeId))
- for ($i = 0; $i <= 20; $i++)
- if ($subClassMask & (1 << $i))
- $subClasses[] = $i;
-
- $itemIds = DB::Aowow()->selectCol('
- SELECT
- id
- FROM
- ?_items
- WHERE
- {spellVisualId IN (?a) OR }
- pickUpSoundId = ?d OR
- dropDownSoundId = ?d OR
- sheatheSoundId = ?d OR
- unsheatheSoundId = ?d {OR
- (
- IF (soundOverrideSubclass > 0, soundOverrideSubclass, subclass) IN (?a) AND
- class = ?d
- )}
- ', $displayIds ?: DBSIMPLE_SKIP, $this->typeId, $this->typeId, $this->typeId, $this->typeId, $subClasses ?: DBSIMPLE_SKIP, ITEM_CLASS_WEAPON);
- if ($itemIds)
- {
- $items = new ItemList(array(['id', $itemIds]));
- if (!$items->error)
- {
- $this->extendGlobalData($items->getJSGlobals(GLOBALINFO_SELF));
- $this->lvTabs[] = ['item', ['data' => array_values($items->getListviewData())]];
- }
- }
-
-
- // tab: Zones
- if ($zoneIds = DB::Aowow()->select('SELECT id, worldStateId, worldStateValue FROM ?_zones_sounds WHERE ambienceDay = ?d OR ambienceNight = ?d OR musicDay = ?d OR musicNight = ?d OR intro = ?d', $this->typeId, $this->typeId, $this->typeId, $this->typeId, $this->typeId))
- {
- $zones = new ZoneList(array(['id', array_column($zoneIds, 'id')]));
- if (!$zones->error)
- {
- $this->extendGlobalData($zones->getJSGlobals(GLOBALINFO_SELF));
-
- $zoneData = $zones->getListviewData();
- $parents = $zones->getAllFields('parentArea');
- $tabData = [];
- $pIds = array_filter(array_unique(array_values($parents)));
- if ($pIds)
- {
- $pZones = new ZoneList(array(['id', $pIds]));
- if (!$pZones->error)
- {
- $this->extendGlobalData($pZones->getJSGlobals(GLOBALINFO_SELF));
-
- $pData = $pZones->getListviewData();
- foreach ($parents as $child => $parent)
- {
- if (!$parent || empty($pData[$parent]))
- continue;
-
- if (!isset($pData[$parent]['subzones']))
- $pData[$parent]['subzones'] = [];
-
- $pData[$parent]['subzones'][] = $child;
- unset($parents[$child]);
- }
-
- // these are original parents
- foreach ($parents as $parent => $__)
- if (empty($pData[$parent]))
- $pData[$parent] = $zoneData[$parent];
-
- $zoneData = $pData;
- }
- }
-
- if (array_filter(array_column($zoneIds, 'worldStateId')))
- {
- $tabData['extraCols'] = ['$Listview.extraCols.condition'];
-
- foreach ($zoneIds as $zData)
- if ($zData['worldStateId'])
- $zoneData[$zData['id']]['condition'][0][$this->typeId][] = [[CND_WORLD_STATE, $zData['worldStateId'], $zData['worldStateValue']]];
- }
-
- $tabData['data'] = array_values($zoneData);
- $tabData['hiddenCols'] = ['territory'];
-
- $this->lvTabs[] = ['zone', $tabData];
- }
- }
-
-
- // tab: Races (VocalUISounds (containing error voice overs))
- if ($vo = DB::Aowow()->selectCol('SELECT raceId FROM ?_races_sounds WHERE soundId = ?d GROUP BY raceId', $this->typeId))
- {
- $races = new CharRaceList(array(['id', $vo]));
- if (!$races->error)
- {
- $this->extendGlobalData($races->getJSGlobals(GLOBALINFO_SELF));
- $this->lvTabs[] = ['race', ['data' => array_values($races->getListviewData())]];
- }
- }
-
-
- // tab: Emotes (EmotesTextSound (containing emote audio))
- if ($em = DB::Aowow()->selectCol('SELECT emoteId FROM ?_emotes_sounds WHERE soundId = ?d GROUP BY emoteId', $this->typeId))
- {
- $races = new EmoteList(array(['id', $em]));
- if (!$races->error)
- {
- $this->extendGlobalData($races->getJSGlobals(GLOBALINFO_SELF));
- $this->lvTabs[] = ['emote', array(
- 'data' => array_values($races->getListviewData()),
- 'name' => Util::ucFirst(Lang::game('emotes'))
- ), 'emote'];
- }
- }
-
- $creatureIds = DB::World()->selectCol('SELECT ct.CreatureID FROM creature_text ct LEFT JOIN broadcast_text bct ON bct.ID = ct.BroadCastTextId WHERE bct.SoundEntriesID = ?d OR ct.Sound = ?d', $this->typeId, $this->typeId);
-
- // can objects or areatrigger play sound...?
- if ($goosp = SmartAI::getOwnerOfSoundPlayed($this->typeId, Type::NPC))
- $creatureIds = array_merge($creatureIds, $goosp[Type::NPC]);
-
- // tab: NPC (dialogues...?, generic creature sound)
- // skipping (always empty): transforms, footsteps
- $displayIds = DB::Aowow()->selectCol('
- SELECT id FROM ?_creature_sounds WHERE
- greeting = ?d OR
- farewell = ?d OR
- angry = ?d OR
- exertion = ?d OR
- exertioncritical = ?d OR
- injury = ?d OR
- injurycritical = ?d OR
- death = ?d OR
- stun = ?d OR
- stand = ?d OR
- aggro = ?d OR
- wingflap = ?d OR
- wingglide = ?d OR
- alert = ?d OR
- fidget = ?d OR
- customattack = ?d OR
- `loop` = ?d OR
- jumpstart = ?d OR
- jumpend = ?d OR
- petattack = ?d OR
- petorder = ?d OR
- petdismiss = ?d OR
- birth = ?d OR
- spellcast = ?d OR
- submerge = ?d OR
- submerged = ?d
- ', $this->typeId, $this->typeId, $this->typeId, $this->typeId, $this->typeId, $this->typeId, $this->typeId, $this->typeId, $this->typeId, $this->typeId, $this->typeId, $this->typeId, $this->typeId, $this->typeId, $this->typeId, $this->typeId, $this->typeId, $this->typeId, $this->typeId, $this->typeId, $this->typeId, $this->typeId, $this->typeId, $this->typeId, $this->typeId, $this->typeId);
-
- // broadcast_text <-> creature_text
- if ($creatureIds || $displayIds)
- {
- $extra = [];
- $cnds = [CFG_SQL_LIMIT_NONE, &$extra];
- if (!User::isInGroup(U_GROUP_STAFF))
- $cnds[] = [['cuFlags', CUSTOM_EXCLUDE_FOR_LISTVIEW, '&'], 0];
-
- if ($creatureIds)
- $extra[] = ['id', $creatureIds];
-
- if ($displayIds)
- $extra[] = ['displayId1', $displayIds];
-
- if (count($extra) > 1)
- array_unshift($extra, 'OR');
- else
- $extra = array_pop($extra);
-
- $npcs = new CreatureList($cnds);
- if (!$npcs->error)
- {
- $this->addScript([JS_FILE, '?data=zones&locale='.User::$localeId.'&t='.$_SESSION['dataKey']]);
-
- $this->extendGlobalData($npcs->getJSGlobals(GLOBALINFO_SELF));
- $this->lvTabs[] = ['creature', ['data' => array_values($npcs->getListviewData())]];
- }
- }
- }
-}
-
-
-?>
diff --git a/pages/sounds.php b/pages/sounds.php
deleted file mode 100644
index ea7e382f..00000000
--- a/pages/sounds.php
+++ /dev/null
@@ -1,88 +0,0 @@
- ['filter' => FILTER_UNSAFE_RAW]];
-
- public function __construct($pageCall, $pageParam)
- {
- $this->getCategoryFromUrl($pageParam);;
- if (isset($this->category[0]))
- header('Location: ?sounds&filter=ty='.$this->category[0], true, 302);
-
- $this->filterObj = new SoundListFilter();
-
- parent::__construct($pageCall, $pageParam);
-
- $this->name = Util::ucFirst(Lang::game('sounds'));
- }
-
- protected function generateContent()
- {
- $this->redButtons = array(
- BUTTON_WOWHEAD => true,
- BUTTON_PLAYLIST => true
- );
-
- $conditions = [];
- if ($_ = $this->filterObj->getConditions())
- $conditions[] = $_;
-
- $this->filter = $this->filterObj->getForm();
- $this->filter['query'] = $this->_get['filter'];
-
- $tabData = [];
- $sounds = new SoundList($conditions);
- if (!$sounds->error)
- {
- $tabData['data'] = array_values($sounds->getListviewData());
-
- // create note if search limit was exceeded; overwriting 'note' is intentional
- if ($sounds->getMatches() > CFG_SQL_LIMIT_DEFAULT)
- {
- $tabData['note'] = sprintf(Util::$tryFilteringString, 'LANG.lvnote_soundsfound', $sounds->getMatches(), CFG_SQL_LIMIT_DEFAULT);
- $tabData['_truncated'] = 1;
- }
-
- if ($this->filterObj->error)
- $tabData['_errors'] = 1;
- }
- $this->lvTabs[] = ['sound', $tabData];
-
- Lang::sort('sound', 'cat');
- }
-
- protected function generateTitle()
- {
- array_unshift($this->title, $this->name);
-
- $form = $this->filterObj->getForm();
- if (isset($form['ty']) && count($form['ty']) == 1)
- array_unshift($this->title, Lang::sound('cat', $form['ty'][0]));
- }
-
- protected function generatePath()
- {
- $form = $this->filterObj->getForm();
- if (isset($form['ty']) && count($form['ty']) == 1)
- $this->path[] = $form['ty'];
- }
-}
-
-?>
diff --git a/pages/spell.php b/pages/spell.php
deleted file mode 100644
index 195668cb..00000000
--- a/pages/spell.php
+++ /dev/null
@@ -1,2240 +0,0 @@
- ['filter' => FILTER_CALLBACK, 'options' => 'GenericPage::checkDomain']];
-
- private $difficulties = [];
- private $firstRank = 0;
- private $powerTpl = '$WowheadPower.registerSpell(%d, %d, %s);';
-
- public function __construct($pageCall, $id)
- {
- parent::__construct($pageCall, $id);
-
- // temp locale
- if ($this->mode == CACHE_TYPE_TOOLTIP && $this->_get['domain'])
- Util::powerUseLocale($this->_get['domain']);
-
- $this->typeId = intVal($id);
-
- $this->subject = new SpellList(array(['id', $this->typeId]));
- if ($this->subject->error)
- $this->notFound(Lang::game('spell'), Lang::spell('notFound'));
-
- $jsg = $this->subject->getJSGlobals(GLOBALINFO_ANY, $extra);
- $this->extendGlobalData($jsg, $extra);
-
- $this->name = $this->subject->getField('name', true);
-
- // has difficulty versions of itself
- $this->difficulties = DB::Aowow()->selectRow(
- 'SELECT normal10 AS "0", normal25 AS "1",
- heroic10 AS "2", heroic25 AS "3"
- FROM ?_spelldifficulty
- WHERE normal10 = ?d OR normal25 = ?d OR
- heroic10 = ?d OR heroic25 = ?d',
- $this->typeId, $this->typeId, $this->typeId, $this->typeId
- );
-
- // returns self or firstRank
- $this->firstRank = DB::Aowow()->selectCell(
- 'SELECT IF(s1.RankNo <> 1 AND s2.id, s2.id, s1.id)
- FROM ?_spell s1
- LEFT JOIN ?_spell s2
- ON s1.SpellFamilyId = s2.SpelLFamilyId AND s1.SpellFamilyFlags1 = s2.SpelLFamilyFlags1 AND
- s1.SpellFamilyFlags2 = s2.SpellFamilyFlags2 AND s1.SpellFamilyFlags3 = s2.SpellFamilyFlags3 AND
- s1.name_loc0 = s2.name_loc0 AND s2.RankNo = 1
- WHERE s1.id = ?d',
- $this->typeId
- );
- }
-
- protected function generatePath()
- {
- $cat = $this->subject->getField('typeCat');
- $cf = $this->subject->getField('cuFlags');
-
- $this->path[] = $cat;
-
- // reconstruct path
- switch ($cat)
- {
- case -2:
- case 7:
- case -13:
- if ($cl = $this->subject->getField('reqClassMask'))
- $this->path[] = log($cl, 2) + 1;
- else if ($cl = array_search($this->subject->getField('spellFamilyId'), Game::$class2SpellFamily))
- $this->path[] = $cl;
-
- if ($cat == -13)
- $this->path[] = ($cf & (SPELL_CU_GLYPH_MAJOR | SPELL_CU_GLYPH_MINOR)) >> 6;
- else
- $this->path[] = $this->subject->getField('skillLines')[0];
-
- break;
- case 9:
- case -3:
- case 11:
- $this->path[] = $this->subject->getField('skillLines')[0];
-
- if ($cat == 11)
- if ($_ = $this->subject->getField('reqSpellId'))
- $this->path[] = $_;
-
- break;
- case -11:
- foreach (SpellList::$skillLines as $line => $skills)
- if (in_array($this->subject->getField('skillLines')[0], $skills))
- $this->path[] = $line;
- break;
- case -7: // only spells unique in skillLineAbility will always point to the right skillLine :/
- if ($cf & SPELL_CU_PET_TALENT_TYPE0)
- $this->path[] = 411; // Ferocity
- else if ($cf & SPELL_CU_PET_TALENT_TYPE1)
- $this->path[] = 409; // Tenacity
- else if ($cf & SPELL_CU_PET_TALENT_TYPE2)
- $this->path[] = 410; // Cunning
- break;
- case -5:
- if ($this->subject->getField('effect2AuraId') == 207 || $this->subject->getField('effect3AuraId') == 207)
- $this->path[] = 2; // flying (also contains 32, so checked first)
- else if ($this->subject->getField('effect2AuraId') == 32 || $this->subject->getField('effect3AuraId') == 32)
- $this->path[] = 1; // ground
- else
- $this->path[] = 3; // misc
- }
- }
-
- protected function generateTitle()
- {
- array_unshift($this->title, $this->name, Util::ucFirst(Lang::game('spell')));
- }
-
- protected function generateContent()
- {
- $this->addScript([JS_FILE, '?data=zones&locale='.User::$localeId.'&t='.$_SESSION['dataKey']]);
-
- $_cat = $this->subject->getField('typeCat');
-
- $redButtons = array(
- BUTTON_VIEW3D => false,
- BUTTON_WOWHEAD => true,
- BUTTON_LINKS => array(
- 'linkColor' => 'ff71d5ff',
- 'linkId' => Type::getFileString(Type::SPELL).':'.$this->typeId,
- 'linkName' => $this->name,
- 'type' => $this->type,
- 'typeId' => $this->typeId
- )
- );
-
- /***********/
- /* Infobox */
- /***********/
-
- $infobox = Lang::getInfoBoxForFlags($this->subject->getField('cuFlags'));
-
- // level
- if (!in_array($_cat, [-5, -6])) // not mount or vanity pet
- {
- if ($_ = $this->subject->getField('talentLevel'))
- $infobox[] = (in_array($_cat, [-2, 7, -13]) ? sprintf(Lang::game('reqLevel'), $_) : Lang::game('level').Lang::main('colon').$_);
- else if ($_ = $this->subject->getField('spellLevel'))
- $infobox[] = (in_array($_cat, [-2, 7, -13]) ? sprintf(Lang::game('reqLevel'), $_) : Lang::game('level').Lang::main('colon').$_);
- }
-
- $jsg = [];
- // races
- if ($_ = Lang::getRaceString($this->subject->getField('reqRaceMask'), $jsg, false))
- {
- $this->extendGlobalIds(Type::CHR_RACE, ...$jsg);
- $t = count($jsg) == 1 ? Lang::game('race') : Lang::game('races');
- $infobox[] = Util::ucFirst($t).Lang::main('colon').$_;
- }
-
- // classes
- if ($_ = Lang::getClassString($this->subject->getField('reqClassMask'), $jsg, false))
- {
- $this->extendGlobalIds(Type::CHR_CLASS, ...$jsg);
- $t = count($jsg) == 1 ? Lang::game('class') : Lang::game('classes');
- $infobox[] = Util::ucFirst($t).Lang::main('colon').$_;
- }
-
- // spell focus
- if ($_ = $this->subject->getField('spellFocusObject'))
- {
- $bar = DB::Aowow()->selectRow('SELECT * FROM ?_spellfocusobject WHERE id = ?d', $_);
- $focus = new GameObjectList(array(['spellFocusId', $_], 1));
- $infobox[] = Lang::game('requires2').' '.($focus->error ? Util::localizedString($bar, 'name') : '[url=?object='.$focus->id.']'.Util::localizedString($bar, 'name').'[/url]');
- }
-
- // primary & secondary trades
- if (in_array($_cat, [9, 11]))
- {
- // skill
- if ($_ = $this->subject->getField('skillLines')[0])
- {
- $rSkill = new SkillList(array(['id', $_]));
- if (!$rSkill->error)
- {
- $this->extendGlobalData($rSkill->getJSGlobals());
-
- $bar = sprintf(Lang::game('requires'), ' [skill='.$rSkill->id.']');
- if ($_ = $this->subject->getField('learnedAt'))
- $bar .= ' ('.$_.')';
-
- $infobox[] = $bar;
- }
- }
-
- // specialization
- if ($_ = $this->subject->getField('reqSpellId'))
- {
- $rSpell = new SpellList(array(['id', $_]));
- if (!$rSpell->error)
- {
- $this->extendGlobalData($rSpell->getJSGlobals());
- $infobox[] = Lang::game('requires2').' [spell='.$rSpell->id.'][/li]';
- }
- }
-
- // difficulty
- if ($_ = $this->subject->getColorsForCurrent())
- $infobox[] = Lang::formatSkillBreakpoints($_);
- }
-
- // accquisition.. 10: starter spell; 7: discovery
- if (isset($this->subject->sources[$this->subject->id][10]))
- $infobox[] = Lang::spell('starter');
- else if (isset($this->subject->sources[$this->subject->id][7]))
- $infobox[] = Lang::spell('discovered');
-
- // training cost
- if ($cost = $this->subject->getField('trainingCost'))
- $infobox[] = Lang::spell('trainingCost').Lang::main('colon').'[money='.$cost.'][/li]';
-
- // icon
- if ($_ = $this->subject->getField('iconId'))
- {
- $infobox[] = Util::ucFirst(lang::game('icon')).Lang::main('colon').'[icondb='.$_.' name=true]';
- $this->extendGlobalIds(Type::ICON, $_);
- }
-
- // used in mode
- foreach ($this->difficulties as $n => $id)
- if ($id == $this->typeId) // "Mode" seems to be multilingual acceptable
- $infobox[] = 'Mode'.Lang::main('colon').Lang::game('modes', $n);
-
- $effects = $this->createEffects($infobox, $redButtons);
-
- // spell script
- if (User::isInGroup(U_GROUP_STAFF))
- if ($_ = DB::World()->selectCell('SELECT ScriptName FROM spell_script_names WHERE ABS(spell_id) = ?d', $this->firstRank))
- $infobox[] = 'Script'.Lang::main('colon').$_;
-
- $infobox = $infobox ? '[ul][li]'.implode('[/li][li]', $infobox).'[/li][/ul]' : '';
-
- // append glyph symbol if available
- $glyphId = 0;
- for ($i = 1; $i < 4; $i++)
- if ($this->subject->getField('effect'.$i.'Id') == 74)
- $glyphId = $this->subject->getField('effect'.$i.'MiscValue');
-
- if ($_ = DB::Aowow()->selectCell('SELECT ic.name FROM ?_glyphproperties gp JOIN ?_icons ic ON gp.iconId = ic.id WHERE gp.spellId = ?d { OR gp.id = ?d }', $this->typeId, $glyphId ?: DBSIMPLE_SKIP))
- if (file_exists('static/images/wow/Interface/Spellbook/'.$_.'.png'))
- $infobox .= '[img src='.STATIC_URL.'/images/wow/Interface/Spellbook/'.$_.'.png border=0 float=center margin=15]';
-
-
- /****************/
- /* Main Content */
- /****************/
-
- $this->reagents = $this->createReagentList();
- $this->scaling = $this->createScalingData();
- $this->items = $this->createRequiredItems();
- $this->tools = $this->createTools();
- $this->effects = $effects;
- $this->attributes = $this->createAttributesList();
- $this->infobox = $infobox;
- $this->powerCost = $this->subject->createPowerCostForCurrent();
- $this->castTime = $this->subject->createCastTimeForCurrent(false, false);
- $this->name = $this->subject->getField('name', true);
- $this->headIcons = [$this->subject->getField('iconString'), $this->subject->getField('stackAmount') ?: ($this->subject->getField('procCharges') > 1 ? $this->subject->getField('procCharges') : '')];
- $this->level = $this->subject->getField('spellLevel');
- $this->rangeName = $this->subject->getField('rangeText', true);
- $this->range = $this->subject->getField('rangeMaxHostile');
- $this->gcd = Util::formatTime($this->subject->getField('startRecoveryTime'));
- $this->gcdCat = null; // todo (low): nyi; find out how this works [n/a; normal; ..]
- $this->school = [Util::asHex($this->subject->getField('schoolMask')), Lang::getMagicSchools($this->subject->getField('schoolMask'))];
- $this->dispel = $this->subject->getField('dispelType') ? Lang::game('dt', $this->subject->getField('dispelType')) : null;
- $this->mechanic = $this->subject->getField('mechanic') ? Lang::game('me', $this->subject->getField('mechanic')) : null;
- $this->redButtons = $redButtons;
-
- // minRange exists.. prepend
- if ($_ = $this->subject->getField('rangeMinHostile'))
- $this->range = $_.' - '.$this->range;
-
- if (!($this->subject->getField('attributes2') & 0x80000))
- $this->stances = Lang::getStances($this->subject->getField('stanceMask'));
-
- if (($_ = $this->subject->getField('recoveryTime')) && $_ > 0)
- $this->cooldown = Util::formatTime($_);
- else if (($_ = $this->subject->getField('recoveryCategory')) && $_ > 0)
- $this->cooldown = Util::formatTime($_);
-
- if (($_ = $this->subject->getField('duration')) && $_ > 0)
- $this->duration = Util::formatTime($_);
-
- // factionchange-equivalent
- if ($pendant = DB::World()->selectCell('SELECT IF(horde_id = ?d, alliance_id, -horde_id) FROM player_factionchange_spells WHERE alliance_id = ?d OR horde_id = ?d', $this->typeId, $this->typeId, $this->typeId))
- {
- $altSpell = new SpellList(array(['id', abs($pendant)]));
- if (!$altSpell->error)
- {
- $this->transfer = sprintf(
- Lang::spell('_transfer'),
- $altSpell->id,
- 1, // quality
- $altSpell->getField('iconString'),
- $altSpell->getField('name', true),
- $pendant > 0 ? 'alliance' : 'horde',
- $pendant > 0 ? Lang::game('si', 1) : Lang::game('si', 2)
- );
- }
- }
-
- /**************/
- /* Extra Tabs */
- /**************/
-
- $j = [null, 'A', 'B', 'C'];
-
- $ubSAI = SmartAI::getOwnerOfSpellCast($this->typeId);
-
- // tab: abilities [of shapeshift form]
- for ($i = 1; $i < 4; $i++)
- {
- if ($this->subject->getField('effect'.$i.'AuraId') != 36)
- continue;
-
- $formSpells = DB::Aowow()->selectRow('SELECT spellId1, spellId2, spellId3, spellId4, spellId5, spellId6, spellId7, spellId8 FROM ?_shapeshiftforms WHERE id = ?d', $this->subject->getField('effect'.$i.'MiscValue'));
- if (!$formSpells)
- continue;
-
- $abilities = new SpellList(array(['id', $formSpells]));
- if (!$abilities->error)
- {
- $tabData = array(
- 'data' => array_values($abilities->getListviewData()),
- 'id' => 'controlledabilities',
- 'name' => '$LANG.tab_controlledabilities',
- 'visibleCols' => ['level'],
- );
-
- if (!$abilities->hasSetFields(['skillLines']))
- $tabData['hiddenCols'] = ['skill'];
-
- $this->lvTabs[] = ['spell', $tabData];
-
- $this->extendGlobalData($abilities->getJSGlobals(GLOBALINFO_SELF));
- }
- }
-
- // tab: [$this] modifies
- $sub = ['OR'];
- $conditions = [
- ['s.typeCat', [0, -9, -8], '!'], // uncategorized (0), GM (-9), NPC-Spell (-8); NPC includes totems, lightwell and others :/
- ['s.spellFamilyId', $this->subject->getField('spellFamilyId')],
- &$sub
- ];
-
- for ($i = 1; $i < 4; $i++)
- {
- // include dummy..? (4)
- if (!in_array($this->subject->getField('effect'.$i.'AuraId'), [107, 108, 256, 286, 195, 262, 263, 272, 274, 275, 316 /*, 4*/]))
- continue;
-
- $m1 = $this->subject->getField('effect1SpellClassMask'.$j[$i]);
- $m2 = $this->subject->getField('effect2SpellClassMask'.$j[$i]);
- $m3 = $this->subject->getField('effect3SpellClassMask'.$j[$i]);
-
- if (!$m1 && !$m2 && !$m3)
- continue;
-
- $sub[] = ['s.spellFamilyFlags1', $m1, '&'];
- $sub[] = ['s.spellFamilyFlags2', $m2, '&'];
- $sub[] = ['s.spellFamilyFlags3', $m3, '&'];
- }
-
- if (count($sub) > 1)
- {
- $modSpells = new SpellList($conditions);
- if (!$modSpells->error)
- {
- $tabData = array(
- 'data' => array_values($modSpells->getListviewData()),
- 'id' => 'modifies',
- 'name' => '$LANG.tab_modifies',
- 'visibleCols' => ['level'],
- );
-
- if (!$modSpells->hasSetFields(['skillLines']))
- $tabData['hiddenCols'] = ['skill'];
-
- $this->lvTabs[] = ['spell', $tabData];
-
- $this->extendGlobalData($modSpells->getJSGlobals(GLOBALINFO_SELF | GLOBALINFO_RELATED));
- }
- }
-
- // tab: [$this is] modified by
- $sub = ['OR'];
- $conditions = [
- ['s.spellFamilyId', $this->subject->getField('spellFamilyId')],
- &$sub
- ];
-
- for ($i = 1; $i < 4; $i++)
- {
- $m1 = $this->subject->getField('spellFamilyFlags1');
- $m2 = $this->subject->getField('spellFamilyFlags2');
- $m3 = $this->subject->getField('spellFamilyFlags3');
-
- if (!$m1 && !$m2 && !$m3)
- continue;
-
- $sub[] = array(
- 'AND',
- ['s.effect'.$i.'AuraId', [107, 108, 256, 286, 195, 262, 263, 272, 274, 275, 316 /*, 4*/]],
- [
- 'OR',
- ['s.effect1SpellClassMask'.$j[$i], $m1, '&'],
- ['s.effect2SpellClassMask'.$j[$i], $m2, '&'],
- ['s.effect3SpellClassMask'.$j[$i], $m3, '&']
- ]
- );
- }
-
- if (count($sub) > 1)
- {
- $modsSpell = new SpellList($conditions);
- if (!$modsSpell->error)
- {
- $tabData = array(
- 'data' => array_values($modsSpell->getListviewData()),
- 'id' => 'modified-by',
- 'name' => '$LANG.tab_modifiedby',
- 'visibleCols' => ['level'],
- );
-
- if (!$modsSpell->hasSetFields(['skillLines']))
- $tabData['hiddenCols'] = ['skill'];
-
- $this->lvTabs[] = ['spell', $tabData];
-
- $this->extendGlobalData($modsSpell->getJSGlobals(GLOBALINFO_SELF | GLOBALINFO_RELATED));
- }
- }
-
- // tab: see also
- $conditions = array(
- ['s.schoolMask', $this->subject->getField('schoolMask')],
- ['s.effect1Id', $this->subject->getField('effect1Id')],
- ['s.effect2Id', $this->subject->getField('effect2Id')],
- ['s.effect3Id', $this->subject->getField('effect3Id')],
- ['s.id', $this->subject->id, '!'],
- ['s.name_loc'.User::$localeId, $this->subject->getField('name', true)]
- );
-
- $saSpells = new SpellList($conditions);
- if (!$saSpells->error)
- {
- $data = $saSpells->getListviewData();
- if ($this->difficulties) // needs a way to distinguish between dungeon and raid :x; creature using this -> map -> areaType?
- {
- $saE = ['$Listview.extraCols.mode'];
-
- foreach ($data as $id => &$d)
- {
- $d['modes'] = ['mode' => 0];
-
- if ($this->difficulties[0] == $id) // b0001000
- {
- if (!$this->difficulties[2] && !$this->difficulties[3])
- $d['modes']['mode'] |= 0x2;
- else
- $d['modes']['mode'] |= 0x8;
- }
-
- if ($this->difficulties[1] == $id) // b0010000
- {
- if (!$this->difficulties[2] && !$this->difficulties[3])
- $d['modes']['mode'] |= 0x1;
- else
- $d['modes']['mode'] |= 0x10;
- }
-
- if ($this->difficulties[2] == $id) // b0100000
- $d['modes']['mode'] |= 0x20;
-
- if ($this->difficulties[3] == $id) // b1000000
- $d['modes']['mode'] |= 0x40;
- }
- }
-
- $tabData = array(
- 'data' => array_values($data),
- 'id' => 'see-also',
- 'name' => '$LANG.tab_seealso',
- 'visibleCols' => ['level'],
- );
-
- if (!$saSpells->hasSetFields(['skillLines']))
- $tabData['hiddenCols'] = ['skill'];
-
- if (isset($saE))
- $tabData['extraCols'] = $saE;
-
- $this->lvTabs[] = ['spell', $tabData];
-
- $this->extendGlobalData($saSpells->getJSGlobals(GLOBALINFO_SELF | GLOBALINFO_RELATED));
- }
-
- // tab: shared cooldown
- if ($this->subject->getField('recoveryCategory'))
- {
- $conditions = array(
- ['id', $this->typeId, '!'],
- ['category', $this->subject->getField('category')],
- ['recoveryCategory', 0, '>'],
- );
-
- // limit shared cooldowns to same player class for regulat users
- if (!User::isInGroup(U_GROUP_STAFF) && $this->subject->getField('spellFamilyId'))
- $conditions[] = ['spellFamilyId', $this->subject->getField('spellFamilyId')];
-
- $cdSpells = new SpellList($conditions);
- if (!$cdSpells->error)
- {
- $this->lvTabs[] = ['spell', array(
- 'data' => array_values($cdSpells->getListviewData()),
- 'name' => '$LANG.tab_sharedcooldown',
- 'id' => 'shared-cooldown'
- )];
-
- $this->extendGlobalData($cdSpells->getJSGlobals(GLOBALINFO_SELF | GLOBALINFO_RELATED));
- }
- }
-
- // tab: used by - spell
- if ($so = DB::Aowow()->selectCell('SELECT id FROM ?_spelloverride WHERE spellId1 = ?d OR spellId2 = ?d OR spellId3 = ?d OR spellId4 = ?d OR spellId5 = ?d', $this->subject->id, $this->subject->id, $this->subject->id, $this->subject->id, $this->subject->id))
- {
- $conditions = array(
- 'OR',
- ['AND', ['effect1AuraId', 293], ['effect1MiscValue', $so]],
- ['AND', ['effect2AuraId', 293], ['effect2MiscValue', $so]],
- ['AND', ['effect3AuraId', 293], ['effect3MiscValue', $so]]
- );
- $ubSpells = new SpellList($conditions);
- if (!$ubSpells->error)
- {
- $this->lvTabs[] = ['spell', array(
- 'data' => array_values($ubSpells->getListviewData()),
- 'id' => 'used-by-spell',
- 'name' => '$LANG.tab_usedby'
- )];
-
- $this->extendGlobalData($ubSpells->getJSGlobals(GLOBALINFO_SELF));
- }
- }
-
-
- // tab: used by - itemset
- $conditions = array(
- 'OR',
- ['spell1', $this->subject->id], ['spell2', $this->subject->id], ['spell3', $this->subject->id], ['spell4', $this->subject->id],
- ['spell5', $this->subject->id], ['spell6', $this->subject->id], ['spell7', $this->subject->id], ['spell8', $this->subject->id]
- );
-
- $ubSets = new ItemsetList($conditions);
- if (!$ubSets->error)
- {
- $this->lvTabs[] = ['itemset', array(
- 'data' => array_values($ubSets->getListviewData()),
- 'id' => 'used-by-itemset',
- 'name' => '$LANG.tab_usedby'
- )];
-
- $this->extendGlobalData($ubSets->getJSGlobals(GLOBALINFO_SELF | GLOBALINFO_RELATED));
- }
-
- // tab: used by - item
- $conditions = array(
- 'OR', // 6: learn spell
- ['AND', ['spellTrigger1', 6, '!'], ['spellId1', $this->subject->id]],
- ['AND', ['spellTrigger2', 6, '!'], ['spellId2', $this->subject->id]],
- ['AND', ['spellTrigger3', 6, '!'], ['spellId3', $this->subject->id]],
- ['AND', ['spellTrigger4', 6, '!'], ['spellId4', $this->subject->id]],
- ['AND', ['spellTrigger5', 6, '!'], ['spellId5', $this->subject->id]]
- );
-
- $ubItems = new ItemList($conditions);
- if (!$ubItems->error)
- {
- $this->lvTabs[] = ['item', array(
- 'data' => array_values($ubItems->getListviewData()),
- 'id' => 'used-by-item',
- 'name' => '$LANG.tab_usedby'
- )];
-
- $this->extendGlobalData($ubItems->getJSGlobals(GLOBALINFO_SELF));
- }
-
- // tab: used by - object
- $conditions = array(
- 'OR',
- ['onUseSpell', $this->subject->id], ['onSuccessSpell', $this->subject->id],
- ['auraSpell', $this->subject->id], ['triggeredSpell', $this->subject->id]
- );
- if (!empty($ubSAI[Type::OBJECT]))
- $conditions[] = ['id', $ubSAI[Type::OBJECT]];
-
- $ubObjects = new GameObjectList($conditions);
- if (!$ubObjects->error)
- {
- $this->lvTabs[] = ['object', array(
- 'data' => array_values($ubObjects->getListviewData()),
- 'id' => 'used-by-object',
- 'name' => '$LANG.tab_usedby'
- )];
-
- $this->extendGlobalData($ubObjects->getJSGlobals());
- }
-
- // tab: used by - areatrigger
- if (User::isInGroup(U_GROUP_EMPLOYEE))
- {
- if (!empty($ubSAI[Type::AREATRIGGER]))
- {
- $ubTriggers = new AreaTriggerList(array(['id', $ubSAI[Type::AREATRIGGER]]));
- if (!$ubTriggers->error)
- {
- $this->lvTabs[] = ['areatrigger', array(
- 'data' => array_values($ubTriggers->getListviewData()),
- 'id' => 'used-by-areatrigger',
- 'name' => '$LANG.tab_usedby'
- ), 'areatrigger'];
- }
- }
- }
-
- // tab: criteria of
- $conditions = array(
- ['ac.type', [ACHIEVEMENT_CRITERIA_TYPE_BE_SPELL_TARGET, ACHIEVEMENT_CRITERIA_TYPE_BE_SPELL_TARGET2, ACHIEVEMENT_CRITERIA_TYPE_CAST_SPELL,
- ACHIEVEMENT_CRITERIA_TYPE_CAST_SPELL2, ACHIEVEMENT_CRITERIA_TYPE_LEARN_SPELL]
- ],
- ['ac.value1', $this->typeId]
- );
- $coAchievemnts = new AchievementList($conditions);
- if (!$coAchievemnts->error)
- {
- $this->lvTabs[] = ['achievement', array(
- 'data' => array_values($coAchievemnts->getListviewData()),
- 'id' => 'criteria-of',
- 'name' => '$LANG.tab_criteriaof'
- )];
-
- $this->extendGlobalData($coAchievemnts->getJSGlobals(GLOBALINFO_SELF | GLOBALINFO_RELATED));
- }
-
- // tab: contains
- // spell_loot_template & skill_extra_item_template
- $extraItem = DB::World()->selectRow('SELECT * FROM skill_extra_item_template WHERE spellid = ?d', $this->subject->id);
- $spellLoot = new Loot();
-
- if ($spellLoot->getByContainer(LOOT_SPELL, $this->subject->id) || $extraItem)
- {
- $this->extendGlobalData($spellLoot->jsGlobals);
-
- $lv = $spellLoot->getResult();
- $extraCols = $spellLoot->extraCols;
- $extraCols[] = '$Listview.extraCols.percent';
-
- if ($extraItem && $this->subject->canCreateItem())
- {
- $foo = $this->subject->relItems->getListviewData();
-
- for ($i = 1; $i < 4; $i++)
- {
- if (($bar = $this->subject->getField('effect'.$i.'CreateItemId')) && isset($foo[$bar]))
- {
- $lv[$bar] = $foo[$bar];
- $lv[$bar]['percent'] = $extraItem['additionalCreateChance'];
- $lv[$bar]['condition'][0][$this->typeId][] = [[CND_SPELL, $extraItem['requiredSpecialization']]];
- $this->extendGlobalIds(Type::SPELL, $extraItem['requiredSpecialization']);
- $extraCols[] = '$Listview.extraCols.condition';
- if ($max = ($extraItem['additionalMaxNum'] - 1))
- $lv[$bar]['stack'] = [1, $max];
-
- break; // skill_extra_item_template can only contain 1 item
- }
- }
- }
-
- $this->lvTabs[] = ['item', array(
- 'data' => array_values($lv),
- 'name' => '$LANG.tab_contains',
- 'id' => 'contains',
- 'hiddenCols' => ['side', 'slot', 'source', 'reqlevel'],
- 'extraCols' => array_unique($extraCols)
- )];
- }
-
- // tab: exclusive with
- if ($this->firstRank) {
- $linkedSpells = DB::World()->selectCol( // dont look too closely ..... please..?
- 'SELECT IF(sg2.spell_id < 0, sg2.id, sg2.spell_id) AS ARRAY_KEY, IF(sg2.spell_id < 0, sg2.spell_id, sr.stack_rule)
- FROM spell_group sg1
- JOIN spell_group sg2
- ON (sg1.id = sg2.id OR sg1.id = -sg2.spell_id) AND sg1.spell_id != sg2.spell_id
- LEFT JOIN spell_group_stack_rules sr
- ON sg1.id = sr.group_id
- WHERE sg1.spell_id = ?d',
- $this->firstRank
- );
-
- if ($linkedSpells)
- {
- $extraSpells = [];
- foreach ($linkedSpells as $k => $v)
- {
- if ($v > 0)
- continue;
-
- $extraSpells += DB::World()->selectCol( // recursive case (recursive and regular ids are not mixed in a group)
- 'SELECT sg2.spell_id AS ARRAY_KEY, sr.stack_rule
- FROM spell_group sg1
- JOIN spell_group sg2
- ON sg2.id = -sg1.spell_id AND sg2.spell_id != ?d
- LEFT JOIN spell_group_stack_rules sr
- ON sg1.id = sr.group_id
- WHERE sg1.id = ?d',
- $this->firstRank,
- $k
- );
-
- unset($linkedSpells[$k]);
- }
-
- // todo (high): fixme - querys have erronous edge-cases (see spell: 13218)
- if ($groups = $linkedSpells + $extraSpells)
- {
- $stacks = new SpellList(array(['s.id', array_keys($groups)]));
- if (!$stacks->error)
- {
- $data = $stacks->getListviewData();
- foreach ($data as $k => $d)
- $data[$k]['stackRule'] = $groups[$k];
-
- if (!$stacks->hasSetFields(['skillLines']))
- $sH = ['skill'];
-
- $tabData = array(
- 'data' => array_values($data),
- 'id' => 'spell-group-stack',
- 'name' => Lang::spell('stackGroup'),
- 'visibleCols' => ['stackRules']
- );
-
- if (isset($sH))
- $tabData['hiddenCols'] = $sH;
-
- $this->lvTabs[] = ['spell', $tabData];
-
- $this->extendGlobalData($stacks->getJSGlobals(GLOBALINFO_SELF | GLOBALINFO_RELATED));
- }
- }
- }
- }
-
- // tab: linked with
- $rows = DB::World()->select('
- SELECT spell_trigger AS `trigger`,
- spell_effect AS effect,
- type,
- IF(ABS(spell_effect) = ?d, ABS(spell_trigger), ABS(spell_effect)) AS related
- FROM spell_linked_spell
- WHERE ABS(spell_effect) = ?d OR ABS(spell_trigger) = ?d',
- $this->typeId, $this->typeId, $this->typeId
- );
-
- $related = [];
- foreach ($rows as $row)
- $related[] = $row['related'];
-
- if ($related)
- $linked = new SpellList(array(['s.id', $related]));
-
- if (isset($linked) && !$linked->error)
- {
- $lv = $linked->getListviewData();
- $data = [];
-
- foreach ($rows as $r)
- {
- foreach ($lv as $dk => $d)
- {
- if ($r['related'] != $dk)
- continue;
-
- $lv[$dk]['linked'] = [$r['trigger'], $r['effect'], $r['type']];
- $data[] = $lv[$dk];
- break;
- }
- }
-
- $this->lvTabs[] = ['spell', array(
- 'data' => array_values($data),
- 'id' => 'spell-link',
- 'name' => Lang::spell('linkedWith'),
- 'hiddenCols' => ['skill', 'name'],
- 'visibleCols' => ['linkedTrigger', 'linkedEffect']
- )];
-
- $this->extendGlobalData($linked->getJSGlobals(GLOBALINFO_SELF | GLOBALINFO_RELATED));
- }
-
-
- // tab: triggered by
- $conditions = array(
- 'OR',
- ['AND', ['OR', ['effect1Id', SpellList::$effects['trigger']], ['effect1AuraId', SpellList::$auras['trigger']]], ['effect1TriggerSpell', $this->subject->id]],
- ['AND', ['OR', ['effect2Id', SpellList::$effects['trigger']], ['effect2AuraId', SpellList::$auras['trigger']]], ['effect2TriggerSpell', $this->subject->id]],
- ['AND', ['OR', ['effect3Id', SpellList::$effects['trigger']], ['effect3AuraId', SpellList::$auras['trigger']]], ['effect3TriggerSpell', $this->subject->id]],
- );
-
- $trigger = new SpellList($conditions);
- if (!$trigger->error)
- {
- $this->lvTabs[] = ['spell', array(
- 'data' => array_values($trigger->getListviewData()),
- 'id' => 'triggered-by',
- 'name' => '$LANG.tab_triggeredby'
- )];
-
- $this->extendGlobalData($trigger->getJSGlobals(GLOBALINFO_SELF));
- }
-
- // tab: used by - creature
- $conditions = array(
- 'OR',
- ['spell1', $this->typeId], ['spell2', $this->typeId], ['spell3', $this->typeId], ['spell4', $this->typeId],
- ['spell5', $this->typeId], ['spell6', $this->typeId], ['spell7', $this->typeId], ['spell8', $this->typeId]
- );
- if (!empty($ubSAI[Type::NPC]))
- $conditions[] = ['id', $ubSAI[Type::NPC]];
-
- $ubCreature = new CreatureList($conditions);
- if (!$ubCreature->error)
- {
- $this->lvTabs[] = ['creature', array(
- 'data' => array_values($ubCreature->getListviewData()),
- 'id' => 'used-by-npc',
- 'name' => '$LANG.tab_usedby'
- )];
-
- $this->extendGlobalData($ubCreature->getJSGlobals(GLOBALINFO_SELF));
- }
-
- // tab: zone
- if ($areas = DB::World()->select('SELECT * FROM spell_area WHERE spell = ?d', $this->typeId))
- {
- $zones = new ZoneList(array(['id', array_column($areas, 'area')]));
- if (!$zones->error)
- {
- $lvZones = $zones->getListviewData();
- $this->extendGlobalData($zones->getJSGlobals());
-
- $lv = [];
- $parents = [];
- $extra = false;
- foreach ($areas as $a)
- {
- if (empty($lvZones[$a['area']]))
- continue;
-
- $condition = [];
- if ($a['aura_spell'])
- {
- $this->extendGlobalIds(Type::SPELL, abs($a['aura_spell']));
- $condition[0][$this->typeId][] = [[$a['aura_spell'] > 0 ? CND_AURA : -CND_AURA, abs($a['aura_spell'])]];
- }
-
- if ($a['quest_start']) // status for quests needs work
- {
- $this->extendGlobalIds(Type::QUEST, $a['quest_start']);
- $group = [];
- for ($i = 0; $i < 7; $i++)
- {
- if (!($a['quest_start_status'] & (1 << $i)))
- continue;
-
- if ($i == 0)
- $group[] = [CND_QUEST_NONE, $a['quest_start']];
- else if ($i == 1)
- $group[] = [CND_QUEST_COMPLETE, $a['quest_start']];
- else if ($i == 3)
- $group[] = [CND_QUESTTAKEN, $a['quest_start']];
- else if ($i == 6)
- $group[] = [CND_QUESTREWARDED, $a['quest_start']];
- }
-
- if ($group)
- $condition[0][$this->typeId][] = $group;
- }
-
- if ($a['quest_end'] && $a['quest_end'] != $a['quest_start'])
- {
- $this->extendGlobalIds(Type::QUEST, $a['quest_end']);
- $group = [];
- for ($i = 0; $i < 7; $i++)
- {
- if (!($a['quest_end_status'] & (1 << $i)))
- continue;
-
- if ($i == 0)
- $group[] = [-CND_QUEST_NONE, $a['quest_end']];
- else if ($i == 1)
- $group[] = [-CND_QUEST_COMPLETE, $a['quest_end']];
- else if ($i == 3)
- $group[] = [-CND_QUESTTAKEN, $a['quest_end']];
- else if ($i == 6)
- $group[] = [-CND_QUESTREWARDED, $a['quest_end']];
- }
-
- if ($group)
- $condition[0][$this->typeId][] = $group;
- }
-
- if ($a['racemask'])
- {
- $foo = [];
- for ($i = 0; $i < 11; $i++)
- if ($a['racemask'] & (1 << $i))
- $foo[] = $i + 1;
-
- $this->extendGlobalIds(Type::CHR_RACE, ...$foo);
- $condition[0][$this->typeId][] = [[CND_RACE, $a['racemask']]];
- }
-
- if ($a['gender'] != 2) // 2: both
- $condition[0][$this->typeId][] = [[CND_GENDER, $a['gender'] + 1]];
-
- $row = $lvZones[$a['area']];
- if ($condition)
- {
- $extra = true;
- $row = array_merge($row, ['condition' => $condition]);
- }
-
- // merge subzones, into one row, if: conditions match && parentZone is shared
- if ($p = $zones->getEntry($a['area'])['parentArea'])
- {
- $parents[] = $p;
- $row['parentArea'] = $p;
- $row['subzones'] = [$a['area']];
- }
- else
- $row['parentArea'] = 0;
-
- $set = false;
- foreach ($lv as &$v)
- {
- if ($v['parentArea'] != $row['parentArea'] && $v['id'] != $row['parentArea'])
- continue;
-
- if (empty($v['condition']) xor empty($row['condition']))
- continue;
-
- if (!empty($row['condition']) && !empty($v['condition']) && $v['condition'] != $row['condition'])
- continue;
-
- if (!$row['parentArea'] && $v['id'] != $row['parentArea'])
- continue;
-
- $set = true;
- $v['subzones'][] = $row['id'];
- break;
- }
-
- // add self as potential subzone; IF we are a parentZone without added children, we get filtered in JScript
- if (!$set)
- {
- $row['subzones'] = [$row['id']];
- $lv[] = $row;
- }
- }
-
- // overwrite lvData with parent-lvData (condition and subzones are kept)
- if ($parents)
- {
- $parents = (new ZoneList(array(['id', $parents])))->getListviewData();
- foreach ($lv as &$_)
- if (isset($parents[$_['parentArea']]))
- $_ = array_merge($_, $parents[$_['parentArea']]);
- }
-
- $tabData = ['data' => array_values($lv)];
-
- if ($extra)
- {
- $tabData['extraCols'] = ['$Listview.extraCols.condition'];
- $tabData['hiddenCols'] = ['instancetype'];
- }
-
- $this->lvTabs[] = ['zone', $tabData];
- }
- }
-
- // tab: teaches
- if ($ids = Game::getTaughtSpells($this->subject))
- {
- $teaches = new SpellList(array(['id', $ids]));
- if (!$teaches->error)
- {
- $this->extendGlobalData($teaches->getJSGlobals(GLOBALINFO_SELF | GLOBALINFO_RELATED));
- $vis = ['level', 'schools'];
-
- foreach ($teaches->iterate() as $__)
- {
- if (!$teaches->canCreateItem())
- continue;
-
- $vis[] = 'reagents';
- break;
- }
-
- $tabData = array(
- 'data' => array_values($teaches->getListviewData()),
- 'id' => 'teaches-spell',
- 'name' => '$LANG.tab_teaches',
- 'visibleCols' => $vis,
- );
-
- if (!$teaches->hasSetFields(['skillLines']))
- $tabData['hiddenCols'] = ['skill'];
-
- $this->lvTabs[] = ['spell', $tabData];
- }
- }
-
- // tab: taught by npc (source:6 => trainer)
- if (!empty($this->subject->sources[$this->typeId][6]))
- {
- $src = $this->subject->sources[$this->typeId][6];
- $list = [];
- if (count($src) == 1 && $src[0] == 1) // multiple trainer
- {
- $list = DB::World()->selectCol('
- SELECT cdt.CreatureId
- FROM creature_default_trainer cdt
- JOIN trainer_spell ts ON ts.TrainerId = cdt.TrainerId
- WHERE ts.SpellId = ?d',
- $this->typeId
- );
- }
- else if ($src)
- $list = array_values($src);
-
- if ($list)
- {
- $tbTrainer = new CreatureList(array(CFG_SQL_LIMIT_NONE, ['ct.id', $list], ['s.guid', null, '!'], ['ct.npcflag', 0x10, '&']));
- if (!$tbTrainer->error)
- {
- $this->extendGlobalData($tbTrainer->getJSGlobals());
- $this->lvTabs[] = ['creature', array(
- 'data' => array_values($tbTrainer->getListviewData()),
- 'id' => 'taught-by-npc',
- 'name' => '$LANG.tab_taughtby',
- )];
- }
- }
- }
-
- // tab: taught by spell
- $conditions = array(
- 'OR',
- ['AND', ['effect1Id', SpellList::$effects['teach']], ['effect1TriggerSpell', $this->subject->id]],
- ['AND', ['effect2Id', SpellList::$effects['teach']], ['effect2TriggerSpell', $this->subject->id]],
- ['AND', ['effect3Id', SpellList::$effects['teach']], ['effect3TriggerSpell', $this->subject->id]],
- );
-
- $tbSpell = new SpellList($conditions);
- $tbsData = [];
- if (!$tbSpell->error)
- {
- $tbsData = $tbSpell->getListviewData();
- $this->lvTabs[] = ['spell', array(
- 'data' => array_values($tbsData),
- 'id' => 'taught-by-spell',
- 'name' => '$LANG.tab_taughtby'
- )];
-
- $this->extendGlobalData($tbSpell->getJSGlobals(GLOBALINFO_SELF));
- }
-
- // tab: taught by quest
- $conditions = ['OR', ['sourceSpellId', $this->typeId], ['rewardSpell', $this->typeId]];
- if ($tbsData)
- {
- $conditions[] = ['rewardSpell', array_keys($tbsData)];
- if (User::isInGroup(U_GROUP_EMPLOYEE))
- $conditions[] = ['rewardSpellCast', array_keys($tbsData)];
- }
- if (User::isInGroup(U_GROUP_EMPLOYEE))
- $conditions[] = ['rewardSpellCast', $this->typeId];
-
- $tbQuest = new QuestList($conditions);
- if (!$tbQuest->error)
- {
- $this->lvTabs[] = ['quest', array(
- 'data' => array_values($tbQuest->getListviewData()),
- 'id' => 'reward-from-quest',
- 'name' => '$LANG.tab_rewardfrom'
- )];
-
- $this->extendGlobalData($tbQuest->getJSGlobals());
- }
-
- // tab: taught by item (i'd like to precheck $this->subject->sources, but there is no source:item only complicated crap like "drop" and "vendor")
- $conditions = array(
- 'OR',
- ['AND', ['spellTrigger1', 6], ['spellId1', $this->subject->id]],
- ['AND', ['spellTrigger2', 6], ['spellId2', $this->subject->id]],
- ['AND', ['spellTrigger3', 6], ['spellId3', $this->subject->id]],
- ['AND', ['spellTrigger4', 6], ['spellId4', $this->subject->id]],
- ['AND', ['spellTrigger5', 6], ['spellId5', $this->subject->id]],
- );
-
- $tbItem = new ItemList($conditions);
- if (!$tbItem->error)
- {
- $this->lvTabs[] = ['item', array(
- 'data' => array_values($tbItem->getListviewData()),
- 'id' => 'taught-by-item',
- 'name' => '$LANG.tab_taughtby'
- )];
-
- $this->extendGlobalData($tbItem->getJSGlobals(GLOBALINFO_SELF));
- }
-
- // tab: enchantments
- $conditions = array(
- 'OR',
- ['AND', ['type1', [1, 3, 7]], ['object1', $this->typeId]],
- ['AND', ['type2', [1, 3, 7]], ['object2', $this->typeId]],
- ['AND', ['type3', [1, 3, 7]], ['object3', $this->typeId]]
- );
- $enchList = new EnchantmentList($conditions);
- if (!$enchList->error)
- {
- $this->lvTabs[] = ['enchantment', array(
- 'data' => array_values($enchList->getListviewData()),
- 'name' => Util::ucFirst(Lang::game('enchantments'))
- ), 'enchantment'];
-
- $this->extendGlobalData($enchList->getJSGlobals());
- }
-
- // tab: sounds
- $activitySounds = DB::Aowow()->selectRow('SELECT * FROM ?_spell_sounds WHERE id = ?d', $this->subject->getField('spellVisualId'));
- array_shift($activitySounds); // remove id-column
- if ($activitySounds)
- {
- $sounds = new SoundList(array(['id', $activitySounds]));
- if (!$sounds->error)
- {
- $data = $sounds->getListviewData();
- foreach ($activitySounds as $activity => $id)
- if (isset($data[$id]))
- $data[$id]['activity'] = $activity; // no index, js wants a string :(
-
- $tabData = ['data' => array_values($data)];
- if ($activitySounds)
- $tabData['visibleCols'] = ['activity'];
-
- $this->extendGlobalData($sounds->getJSGlobals(GLOBALINFO_SELF));
- $this->lvTabs[] = ['sound', $tabData];
- }
- }
-
-
- // find associated NPC, Item and merge results
- // taughtbypets (unused..?)
- // taughtbyquest (usually the spell casted as quest reward teaches something; exclude those seplls from taughtBySpell)
- // taughtbytrainers
- // taughtbyitem
-
- // tab: conditions
- $sc = Util::getServerConditions([CND_SRC_SPELL_LOOT_TEMPLATE, CND_SRC_SPELL_IMPLICIT_TARGET, CND_SRC_SPELL, CND_SRC_SPELL_CLICK_EVENT, CND_SRC_VEHICLE_SPELL, CND_SRC_SPELL_PROC], null, $this->typeId);
- if (!empty($sc[0]))
- {
- $this->extendGlobalData($sc[1]);
- $tab = "";
-
- $this->lvTabs[] = [null, array(
- 'data' => $tab,
- 'id' => 'conditions',
- 'name' => '$LANG.requires'
- )];
- }
- }
-
- protected function generateTooltip()
- {
- $power = new StdClass();
- if (!$this->subject->error)
- {
- [$tooltip, $ttSpells] = $this->subject->renderTooltip();
- [$buff, $bfSpells] = $this->subject->renderBuff();
-
- $power->{'name_'.User::$localeString} = $this->subject->getField('name', true);
- $power->icon = rawurlencode($this->subject->getField('iconString', true, true));
- $power->{'tooltip_'.User::$localeString} = $tooltip;
- $power->{'spells_'.User::$localeString} = $ttSpells;
- $power->{'buff_'.User::$localeString} = $buff;
- $power->{'buffspells_'.User::$localeString} = $bfSpells;
- }
-
- return sprintf($this->powerTpl, $this->typeId, User::$localeId, Util::toJSON($power, JSON_AOWOW_POWER));
- }
-
- private function appendReagentItem(&$reagentResult, $_iId, $_qty, $_mult, $_level, $_path, $alreadyUsed)
- {
- if (in_array($_iId, $alreadyUsed))
- return false;
-
- $item = DB::Aowow()->selectRow('
- SELECT name_loc0, name_loc2, name_loc3, name_loc6, name_loc8, i.id, ic.name AS iconString, quality
- FROM ?_items i
- LEFT JOIN ?_icons ic ON ic.id = i.iconId
- WHERE i.id = ?d',
- $_iId
- );
-
- if (!$item)
- return false;
-
- $this->extendGlobalIds(Type::ITEM, $item['id']);
-
- $_level++;
-
- $data = array(
- 'type' => Type::ITEM,
- 'typeId' => $item['id'],
- 'typeStr' => Type::getFileString(Type::ITEM),
- 'quality' => $item['quality'],
- 'name' => Util::localizedString($item, 'name'),
- 'icon' => $item['iconString'],
- 'qty' => $_qty * $_mult,
- 'path' => $_path.'.'.Type::ITEM.'-'.$item['id'],
- 'level' => $_level
- );
-
- $idx = count($reagentResult);
- $reagentResult[] = $data;
- $alreadyUsed[] = $item['id'];
-
- if (!$this->appendReagentSpell($reagentResult, $item['id'], $data['qty'], $data['level'], $data['path'], $alreadyUsed))
- $reagentResult[$idx]['final'] = true;
-
- return true;
- }
-
- private function appendReagentSpell(&$reagentResult, $_iId, $_qty, $_level, $_path, $alreadyUsed)
- {
- $_level++;
- // assume that tradeSpells only use the first index to create items, so this runs somewhat efficiently >.<
- $spells = DB::Aowow()->select('
- SELECT reagent1, reagent2, reagent3, reagent4, reagent5, reagent6, reagent7, reagent8,
- reagentCount1, reagentCount2, reagentCount3, reagentCount4, reagentCount5, reagentCount6, reagentCount7, reagentCount8,
- name_loc0, name_loc2, name_loc3, name_loc6, name_loc8,
- iconIdBak,
- s.id AS ARRAY_KEY, ic.name AS iconString
- FROM ?_spell s
- JOIN ?_icons ic ON s.iconId = ic.id
- WHERE (effect1CreateItemId = ?d AND effect1Id = 24)',// OR
- // (effect2CreateItemId = ?d AND effect2Id = 24) OR
- // (effect3CreateItemId = ?d AND effect3Id = 24)',
- $_iId //, $_iId, $_iId
- );
-
- if (!$spells)
- return false;
-
- $didAppendSomething = false;
- foreach ($spells as $sId => $row)
- {
- if (in_array(-$sId, $alreadyUsed))
- continue;
-
- $this->extendGlobalIds(Type::SPELL, $sId);
-
- $data = array(
- 'type' => Type::SPELL,
- 'typeId' => $sId,
- 'typeStr' => Type::getFileString(Type::SPELL),
- 'name' => Util::localizedString($row, 'name'),
- 'icon' => $row['iconString'],
- 'qty' => $_qty,
- 'path' => $_path.'.'.Type::SPELL.'-'.$sId,
- 'level' => $_level,
- );
-
- $reagentResult[] = $data;
- $_aU = $alreadyUsed;
- $_aU[] = -$sId;
-
- $hasUnusedReagents = false;
- for ($i = 1; $i < 9; $i++)
- {
- if ($row['reagent'.$i] <= 0 || $row['reagentCount'.$i] <= 0)
- continue;
-
- // handle edge case elemental crafting material: Mote of X + Crystalized X
- // on use items, that has require more reagents of itself
- if ($row['reagentCount'.$i] == 9 && ($row['iconIdBak'] == 140 || $row['iconIdBak'] == 1921))
- $row['reagentCount'.$i]++;
-
- if ($this->appendReagentItem($reagentResult, $row['reagent'.$i], $row['reagentCount'.$i], $data['qty'], $data['level'], $data['path'], $_aU))
- {
- $hasUnusedReagents = true;
- $didAppendSomething = true;
- }
- }
-
- if (!$hasUnusedReagents) // no reagents were added, remove spell from result set
- array_pop($reagentResult);
- }
-
- return $didAppendSomething;
- }
-
- private function createReagentList()
- {
- $reagentResult = [];
- $enhanced = false;
-
- if ($reagents = $this->subject->getReagentsForCurrent())
- {
- foreach ($this->subject->relItems->iterate() as $iId => $__)
- {
- if (!in_array($iId, array_keys($reagents)))
- continue;
-
- $data = array(
- 'type' => Type::ITEM,
- 'typeId' => $iId,
- 'typeStr' => Type::getFileString(Type::ITEM),
- 'quality' => $this->subject->relItems->getField('quality'),
- 'name' => $this->subject->relItems->getField('name', true),
- 'icon' => $this->subject->relItems->getField('iconString'),
- 'qty' => $reagents[$iId][1],
- 'path' => Type::ITEM.'-'.$iId, // id of the html-element
- 'level' => 0 // depths in array, used for indentation
- );
-
- $idx = count($reagentResult);
- $reagentResult[] = $data;
-
- // start with self and current original item in usedEntries (spell < 0; item > 0)
- if ($this->appendReagentSpell($reagentResult, $iId, $data['qty'], 0, $data['path'], [-$this->typeId, $iId]))
- $enhanced = true;
- else
- $reagentResult[$idx]['final'] = true;
- }
- }
-
- // increment all indizes (by prepending null and removing it again)
- array_unshift($reagentResult, null);
- unset($reagentResult[0]);
-
- return [$enhanced, $reagentResult];
- }
-
- private function createScalingData() // calculation mostly like seen in TC
- {
- $scaling = array_merge(
- array(
- 'directSP' => -1,
- 'dotSP' => -1,
- 'directAP' => 0,
- 'dotAP' => 0
- ),
- (array)DB::World()->selectRow('SELECT direct_bonus AS directSP, dot_bonus AS dotSP, ap_bonus AS directAP, ap_dot_bonus AS dotAP FROM spell_bonus_data WHERE entry = ?d', $this->firstRank)
- );
-
- if (!$this->subject->isDamagingSpell() && !$this->subject->isHealingSpell())
- return $scaling;
-
- foreach ($scaling as $k => $v)
- {
- // only calculate for class/pet spells
- if ($v != -1 || !in_array($this->subject->getField('typeCat'), [-2, -3, -7, 7]))
- continue;
-
-
- // no known calculation for physical abilities
- if ($k == 'directAP' || $k == 'dotAP')
- continue;
-
- // dont use spellPower to scale physical Abilities
- if ($this->subject->getField('schoolMask') == 0x1 && ($k == 'directSP' || $k == 'dotSP'))
- continue;
-
- $isDOT = false;
- $pMask = $this->subject->periodicEffectsMask();
-
- if ($k == 'dotSP' || $k == 'dotAP')
- {
- if ($pMask)
- $isDOT = true;
- else
- continue;
- }
- else // if all used effects are periodic, dont calculate direct component
- {
- $bar = true;
- for ($i = 1; $i < 4; $i++)
- {
- if (!$this->subject->getField('effect'.$i.'Id'))
- continue;
-
- if ($pMask & 1 << ($i - 1))
- continue;
-
- $bar = false;
- }
-
- if ($bar)
- continue;
- }
-
- // Damage over Time spells bonus calculation
- $dotFactor = 1.0;
- if ($isDOT)
- {
- $dotDuration = $this->subject->getField('duration');
- // 200% limit
- if ($dotDuration > 0)
- {
- if ($dotDuration > 30000)
- $dotDuration = 30000;
- if (!$this->subject->isChanneledSpell())
- $dotFactor = $dotDuration / 15000;
- }
- }
-
- // Distribute Damage over multiple effects, reduce by AoE
- $castingTime = $this->subject->getCastingTimeForBonus($isDOT);
-
- // 50% for damage and healing spells for leech spells from damage bonus and 0% from healing
- for ($j = 1; $j < 4; ++$j)
- {
- // SPELL_EFFECT_HEALTH_LEECH || SPELL_AURA_PERIODIC_LEECH
- if ($this->subject->getField('effectId'.$j) == 9 || $this->subject->getField('effect'.$j.'AuraId') == 53)
- {
- $castingTime /= 2;
- break;
- }
- }
-
- if ($this->subject->isHealingSpell())
- $castingTime *= 1.88;
-
- // SPELL_SCHOOL_MASK_NORMAL
- if ($this->subject->getField('schoolMask') != 0x1)
- $scaling[$k] = ($castingTime / 3500.0) * $dotFactor;
- else
- $scaling[$k] = 0; // would be 1 ($dotFactor), but we dont want it to be displayed
- }
-
- return $scaling;
- }
-
- private function createRequiredItems()
- {
- // parse itemClass & itemSubClassMask
- $class = $this->subject->getField('equippedItemClass');
- $subClass = $this->subject->getField('equippedItemSubClassMask');
- $invType = $this->subject->getField('equippedItemInventoryTypeMask');
-
- if ($class <= 0)
- return;
-
- $title = ['Class: '.$class, 'SubClass: '.Util::asHex($subClass)];
- $text = Lang::getRequiredItems($class, $subClass, false);
-
- if ($invType)
- {
- // remap some duplicated strings 'Off Hand' and 'Shield' are never used simultaneously
- if ($invType & (1 << INVTYPE_ROBE)) // Robe => Chest
- {
- $invType &= ~(1 << INVTYPE_ROBE);
- $invType |= (1 << INVTYPE_CHEST);
- }
-
- if ($invType & (1 << INVTYPE_RANGEDRIGHT)) // Ranged2 => Ranged
- {
- $invType &= ~(1 << INVTYPE_RANGEDRIGHT);
- $invType |= (1 << INVTYPE_RANGED);
- }
-
- $_ = [];
- $strs = Lang::item('inventoryType');
- foreach ($strs as $k => $str)
- if ($invType & 1 << $k && $str)
- $_[] = $str;
-
- $title[] = Lang::item('slot').Lang::main('colon').Util::asHex($invType);
- $text .= ' '.Lang::spell('_inSlot').Lang::main('colon').implode(', ', $_);
- }
-
- return [$title, $text];
- }
-
- private function createTools()
- {
- $tools = $this->subject->getToolsForCurrent();
-
- // prepare Tools
- foreach ($tools as &$tool)
- {
- if (isset($tool['itemId'])) // Tool
- $tool['url'] = '?item='.$tool['itemId'];
- else // ToolCat
- {
- $tool['quality'] = ITEM_QUALITY_HEIRLOOM - ITEM_QUALITY_NORMAL;
- $tool['url'] = '?items&filter=cr=91;crs='.$tool['id'].';crv=0';
- }
- }
-
- return $tools;
- }
-
- private function createEffects(&$infobox, &$redButtons)
- {
- // proc data .. maybe use more information..?
- $procData = DB::World()->selectRow('SELECT IF(ProcsPerMinute > 0, -ProcsPerMinute, Chance) AS chance, Cooldown AS cooldown FROM spell_proc WHERE ABS(SpellId) = ?d', $this->firstRank);
- if (!isset($procData['cooldown']))
- $procData['cooldown'] = 0;
-
- $effects = [];
- $spellIdx = array_unique(array_merge($this->subject->canTriggerSpell(), $this->subject->canTeachSpell()));
- $itemIdx = $this->subject->canCreateItem();
- $perfItem = DB::World()->selectRow('SELECT * FROM skill_perfect_item_template WHERE spellId = ?d', $this->typeId);
-
- // Iterate through all effects:
- for ($i = 1; $i < 4; $i++)
- {
- if ($this->subject->getField('effect'.$i.'Id') <= 0)
- continue;
-
- $effId = (int)$this->subject->getField('effect'.$i.'Id');
- $effMV = (int)$this->subject->getField('effect'.$i.'MiscValue');
- $effMVB = (int)$this->subject->getField('effect'.$i.'MiscValueB');
- $effBP = (int)$this->subject->getField('effect'.$i.'BasePoints');
- $effDS = (int)$this->subject->getField('effect'.$i.'DieSides');
- $effRPPL = $this->subject->getField('effect'.$i.'RealPointsPerLevel');
- $effAura = (int)$this->subject->getField('effect'.$i.'AuraId');
- $foo = &$effects[$i];
-
- // Icons:
- // .. from item
- if (in_array($i, $itemIdx))
- {
- $_ = $this->subject->getField('effect'.$i.'CreateItemId');
- foreach ($this->subject->relItems->iterate() as $itemId => $__)
- {
- if ($itemId != $_)
- continue;
-
- $foo['icon'] = array(
- 'id' => $this->subject->relItems->id,
- 'name' => $this->subject->relItems->getField('name', true),
- 'quality' => $this->subject->relItems->getField('quality'),
- 'count' => $effDS + $effBP,
- 'icon' => $this->subject->relItems->getField('iconString')
- );
-
- break;
- }
-
- // perfect Items
- if ($perfItem && $this->subject->relItems->getEntry($perfItem['perfectItemType']))
- {
- $cndSpell = new SpellList(array(['id', $perfItem['requiredSpecialization']]));
- if (!$cndSpell->error)
- {
- $foo['perfItem'] = array(
- 'icon' => $cndSpell->getField('iconString'),
- 'quality' => $this->subject->relItems->getField('quality'),
- 'cndSpellId' => $perfItem['requiredSpecialization'],
- 'cndSpellName' => $cndSpell->getField('name', true),
- 'chance' => $perfItem['perfectCreateChance'],
- 'itemId' => $perfItem['perfectItemType'],
- 'itemName' => $this->subject->relItems->getField('name', true)
- );
- }
- }
-
- if ($effDS > 1)
- $foo['icon']['count'] = "'".($effBP + 1).'-'.$foo['icon']['count']."'";
- }
- // .. from spell
- else if (in_array($i, $spellIdx) || in_array($effId, [133, 140, 141]))
- {
- if ($effId == 155)
- $_ = $effMV;
- else
- $_ = $this->subject->getField('effect'.$i.'TriggerSpell');
-
- $trig = new SpellList(array(['s.id', (int)$_]));
-
- $foo['icon'] = array(
- 'id' => $_,
- 'name' => $trig->error ? Util::ucFirst(Lang::game('spell')).' #'.$_ : $trig->getField('name', true),
- 'count' => 0
- );
-
- $this->extendGlobalData($trig->getJSGlobals(GLOBALINFO_SELF | GLOBALINFO_RELATED));
- }
-
- // Effect Name
- if ($_ = Lang::spell('effects', $effId))
- $foo['name'] = (User::isInGroup(U_GROUP_EMPLOYEE) ? sprintf(Util::$dfnString, 'EffectId: '.$effId, $_) : Lang::spell('effects', $effId)).Lang::main('colon');
- else
- $foo['name'] = 'Unknow Effect (#'.$effId.')';
-
- if ($this->subject->getField('effect'.$i.'RadiusMax') > 0)
- $foo['radius'] = $this->subject->getField('effect'.$i.'RadiusMax');
-
- if (!in_array($i, $itemIdx))
- $foo['value'] = ($effDS && $effDS != 1 ? ($effBP + 1).Lang::game('valueDelim') : null).($effBP + $effDS);
-
- if ($effRPPL != 0)
- $foo['value'] = (isset($foo['value']) ? $foo['value'] : '0').sprintf(Lang::spell('costPerLevel'), $effRPPL);
- if ($this->subject->getField('effect'.$i.'Periode') > 0)
- $foo['interval'] = Util::formatTime($this->subject->getField('effect'.$i.'Periode'));
-
- if ($_ = $this->subject->getField('effect'.$i.'Mechanic'))
- $foo['mechanic'] = Lang::game('me', $_);
-
- if (in_array($i, $this->subject->canTriggerSpell()) && !empty($procData['chance']))
- $foo['procData'] = array(
- $procData['chance'],
- $procData['cooldown'] ? Util::formatTime($procData['cooldown'], true) : null
- );
- else if (in_array($i, $this->subject->canTriggerSpell()) && $this->subject->getField('procChance'))
- $foo['procData'] = array(
- $this->subject->getField('procChance'),
- $procData['cooldown'] ? Util::formatTime($procData['cooldown'], true) : null
- );
-
- // parse masks and indizes
- switch ($effId)
- {
- case 8: // Power Drain
- case 30: // Energize
- case 62: // Power Burn
- case 137: // Energize Pct
- $_ = Lang::spell('powerTypes', $effMV);
- if ($_ && User::isInGroup(U_GROUP_EMPLOYEE))
- $_ = sprintf(Util::$dfnString, 'MiscValue'.Lang::main('colon').$effMV, $_);
- else if (!$_)
- $_ = $effMV;
-
- if ($effMV == POWER_RAGE || $effMV == POWER_RUNIC_POWER)
- $foo['value'] = ($effDS && $effDS != 1 ? (($effBP + 1) / 10).Lang::game('valueDelim') : null).(($effBP + $effDS) / 10);
-
- $foo['name'] .= ' ('.$_.')';
- break;
- case 11: // Bind
- if (!$effMV && User::isInGroup(U_GROUP_EMPLOYEE))
- $foo['name'] .= sprintf(Util::$dfnString, 'MiscValue'.Lang::main('colon').$effMV, '(current zone)');
- else if (!$effMV)
- $foo['name'] .= '('.Lang::spell('currentArea').')';
- else if ($_ = ZoneList::getName($effMV))
- $foo['name'] .= '('.$_.')';
- else
- $foo['name'] .= Util::ucFirst(Lang::game('zone')).' #'.$effMV;;
- break;
- case 16: // QuestComplete
- if ($_ = QuestList::getName($effMV))
- $foo['name'] .= '('.$_.')';
- else
- $foo['name'] .= Util::ucFirst(Lang::game('quest')).' #'.$effMV;;
- break;
- case 28: // Summon
- case 56: // Summon Pet
- case 90: // Kill Credit
- case 112: // Summon Demon
- case 134: // Kill Credit2
- if ($summon = $this->subject->getModelInfo($this->typeId, $i))
- $redButtons[BUTTON_VIEW3D] = ['type' => Type::NPC, 'displayId' => $summon['displayId']];
-
- $_ = Lang::game('npc').' #'.$effMV;
- if ($n = CreatureList::getName($effMV))
- $_ = ' ('.$n.')';
-
- $foo['name'] .= $_;
- break;
- case 33: // Open Lock
- $_ = $effMV ? Lang::spell('lockType', $effMV) : $effMV;
- if ($_ && User::isInGroup(U_GROUP_EMPLOYEE))
- $_ = sprintf(Util::$dfnString, 'MiscValue'.Lang::main('colon').$effMV, $_);
- else if (!$_)
- $_ = $effMV;
-
- $foo['name'] .= ' ('.$_.')';
- break;
- case 53: // Enchant Item Perm
- case 54: // Enchant Item Temp
- case 92: // Enchant Held Item
- case 156: // Enchant Item Prismatic
- if ($_ = DB::Aowow()->selectRow('SELECT * FROM ?_itemenchantment WHERE id = ?d', $effMV))
- $foo['name'] .= ' ('.Util::localizedString($_, 'name').')';
- else
- $foo['name'] .= ' #'.$effMV;
- break;
- case 38: // Dispel [miscValue => Types]
- case 126: // Steal Aura
- $_ = Lang::game('dt', $effMV);
- if ($_ && User::isInGroup(U_GROUP_EMPLOYEE))
- $_ = sprintf(Util::$dfnString, 'MiscValue'.Lang::main('colon').$effMV, $_);
- else if (!$_)
- $_ = $effMV;
-
- $foo['name'] .= ' ('.$_.')';
- break;
- case 39: // Learn Language
- $_ = Lang::game('languages', $effMV);
- if ($_ && User::isInGroup(U_GROUP_EMPLOYEE))
- $_ = sprintf(Util::$dfnString, 'MiscValue'.Lang::main('colon').$effMV, $_);
- else if (!$_)
- $_ = $effMV;
-
- $foo['name'] .= ' ('.$_.')';
- break;
- case 50: // Trans Door
- case 76: // Summon Object (Wild)
- case 104: // Summon Object (slot 1)
- case 105: // Summon Object (slot 2)
- case 106: // Summon Object (slot 3)
- case 107: // Summon Object (slot 4)
- if ($summon = $this->subject->getModelInfo($this->typeId, $i))
- $redButtons[BUTTON_VIEW3D] = ['type' => Type::OBJECT, 'displayId' => $summon['displayId']];
-
- $_ = Util::ucFirst(Lang::game('object')).' #'.$effMV;
- if ($n = GameobjectList::getName($effMV))
- $_ = ' ('.$n.')';
-
- $foo['name'] .= $_;
- break;
- case 86: // Activate Object
- $_ = Lang::gameObject('actions', $effMV);
- if ($_ && User::isInGroup(U_GROUP_EMPLOYEE))
- $_ = sprintf(Util::$dfnString, 'MiscValue'.Lang::main('colon').$effMV, $_);
- else if (!$_)
- $_ = $effMV;
-
- $foo['name'] .= ' ('.$_.')';
- break;
- case 74: // Apply Glyph
- if ($_ = DB::Aowow()->selectCell('SELECT spellId FROM ?_glyphproperties WHERE id = ?d', $effMV))
- {
- if ($n = SpellList::getName($_))
- $foo['name'] .= '('.$n.')';
- else
- $foo['name'] .= Util::ucFirst(Lang::game('spell')).' #'.$effMV;
- }
- else
- $foo['name'] .= ' #'.$effMV;;
- break;
- case 95: // Skinning
- switch ($effMV)
- {
- case 0: $_ = Lang::game('ct', 1).', '.Lang::game('ct', 2); break; // Beast, Dragonkin
- case 1:
- case 2: $_ = Lang::game('ct', 4); break; // Elemental (nature based, earth based)
- case 3: $_ = Lang::game('ct', 9); break; // Mechanic
- default; $_ = '';
- }
- if (User::isInGroup(U_GROUP_EMPLOYEE))
- $_ = sprintf(Util::$dfnString, 'MiscValue'.Lang::main('colon').$effMV, $_);
- else
- $_ = $effMV;
-
- $foo['name'] .= ' ('.$_.')';
- break;
- case 108: // Dispel Mechanic
- $_ = Lang::game('me', $effMV);
- if ($_ && User::isInGroup(U_GROUP_EMPLOYEE))
- $_ = sprintf(Util::$dfnString, 'MiscValue'.Lang::main('colon').$effMV, $_);
- else if (!$_)
- $_ = $effMV;
-
- $foo['name'] .= ' ('.$_.')';
- break;
- case 44: // Learn Skill Step
- case 118: // Require Skill
- if ($_ = SkillList::getName($effMV))
- $foo['name'] .= '('.$_.')';
- else
- $foo['name'] .= Util::ucFirst(Lang::game('skill')).' #'.$effMV;;
- break;
- case 146: // Activate Rune
- $_ = Lang::spell('powerRunes', $effMV);
- if ($_ && User::isInGroup(U_GROUP_EMPLOYEE))
- $_ = sprintf(Util::$dfnString, 'MiscValue'.Lang::main('colon').$effMV, $_);
- else if (!$_)
- $_ = $effMV;
-
- $foo['name'] .= ' ('.$_.')';
- break;
- case 131: // Play Music
- case 132: // Play Sound
- $foo['markup'] = '[sound='.$effMV.']';
- break;
- case 103: // Reputation
- $_ = Util::ucFirst(Lang::game('faction')).' #'.$effMV;
- if ($n = FactionList::getName($effMV))
- $_ = ' ('.$n.')';
-
- // apply custom reward rated
- if ($cuRate = DB::World()->selectCell('SELECT spell_rate FROM reputation_reward_rate WHERE spell_rate <> 1 && faction = ?d', $effMV))
- $foo['value'] .= sprintf(Util::$dfnString, Lang::faction('customRewRate'), ' ('.(($cuRate < 1 ? '-' : '+').intVal(($cuRate - 1) * $foo['value'])).')');
-
- $foo['name'] .= $_;
-
- break;
- case 123: // Send Taxi
- $_ = DB::Aowow()->selectRow('
- SELECT tn1.name_loc0 AS start_loc0, tn1.name_loc?d AS start_loc?d, tn2.name_loc0 AS end_loc0, tn2.name_loc?d AS end_loc?d
- FROM ?_taxipath tp
- JOIN ?_taxinodes tn1 ON tp.startNodeId = tn1.id
- JOIN ?_taxinodes tn2 ON tp.endNodeId = tn2.id
- WHERE tp.id = ?d',
- User::$localeId, User::$localeId, User::$localeId, User::$localeId, $effMV
- );
- if ($_ && User::isInGroup(U_GROUP_EMPLOYEE))
- $foo['name'] .= sprintf(Util::$dfnString, 'MiscValue'.Lang::main('colon').$effMV, ' ('.Util::localizedString($_, 'start').''.Util::localizedString($_, 'end').')');
- else if ($_)
- $foo['name'] .= ' ('.Util::localizedString($_, 'start').''.Util::localizedString($_, 'end').')';
- else
- $foo['name'] .= ' ('.$effMV.')';
- break;
- default:
- {
- if (($effMV || $effId == 97) && $effId != 155)
- $foo['name'] .= ' ('.$effMV.')';
-
- break;
- }
- // Aura
- case 6: // Simple
- case 27: // AA Persistent
- case 35: // AA Party
- case 65: // AA Raid
- case 119: // AA Pet
- case 128: // AA Friend
- case 129: // AA Enemy
- case 143: // AA Owner
- {
- if ($effAura > 0 && ($aurName = Lang::spell('auras', $effAura)))
- {
- $foo['name'] .= User::isInGroup(U_GROUP_EMPLOYEE) ? sprintf(Util::$dfnString, 'AuraId: '.$effAura, $aurName) : $aurName;
-
- $bar = $effMV;
- switch ($effAura)
- {
- case 17: // Mod Stealth Detection
- if ($_ = Lang::spell('stealthType', $effMV))
- $bar = User::isInGroup(U_GROUP_EMPLOYEE) ? sprintf(Util::$dfnString, 'MiscValue'.Lang::main('colon').$effMV, $_) : $_;
-
- break;
- case 19: // Mod Invisibility Detection
- if ($_ = Lang::spell('invisibilityType', $effMV))
- $bar = User::isInGroup(U_GROUP_EMPLOYEE) ? sprintf(Util::$dfnString, 'MiscValue'.Lang::main('colon').$effMV, $_) : $_;
-
- break;
- case 24: // Periodic Energize
- case 21: // Obsolete Mod Power
- case 35: // Mod Increase Power
- case 85: // Mod Power Regeneration
- case 110: // Mod Power Regeneration Pct
- case 132: // Mod Increase Energy Percent
- if ($_ = Lang::spell('powerTypes', $effMV))
- $bar = User::isInGroup(U_GROUP_EMPLOYEE) ? sprintf(Util::$dfnString, 'MiscValue'.Lang::main('colon').$effMV, $_) : $_;
-
- break;
- case 29: // Mod Stat
- case 80: // Mod Stat %
- case 137: // Mod Total Stat %
- case 175: // Mod Spell Healing Of Stat Percent
- case 212: // Mod Ranged Attack Power Of Stat Percent
- case 219: // Mod Mana Regeneration from Stat
- case 268: // Mod Attack Power Of Stat Percent
- $mask = $effMV < 0 ? 0x1F : 1 << $effMV;
- $_ = [];
- for ($j = 0; $j < 5; $j++)
- if ($mask & (1 << $j))
- $_[] = Lang::game('stats', $j);
-
- if ($_ = implode(', ', $_));
- $bar = User::isInGroup(U_GROUP_EMPLOYEE) ? sprintf(Util::$dfnString, 'MiscValue'.Lang::main('colon').$effMV, $_) : $_;
-
- break;
- case 36: // Shapeshift
- if ($st = $this->subject->getModelInfo($this->typeId, $i))
- {
- $redButtons[BUTTON_VIEW3D] = array(
- 'type' => Type::NPC,
- 'displayId' => $st['displayId']
- );
-
- if ($st['creatureType'] > 0)
- $infobox[] = Lang::game('type').Lang::main('colon').Lang::game('ct', $st['creatureType']);
-
- if ($_ = $st['displayName'])
- $bar = User::isInGroup(U_GROUP_EMPLOYEE) ? sprintf(Util::$dfnString, 'MiscValue'.Lang::main('colon').$effMV, $_) : $_;
- }
- break;
- case 37: // Effect immunity
- if ($_ = Lang::spell('effects', $effMV))
- $bar = User::isInGroup(U_GROUP_EMPLOYEE) ? sprintf(Util::$dfnString, 'MiscValue'.Lang::main('colon').$effMV, $_) : $_;
-
- break;
- case 38: // Aura immunity
- if ($_ = Lang::spell('auras', $effMV))
- $bar = User::isInGroup(U_GROUP_EMPLOYEE) ? sprintf(Util::$dfnString, 'MiscValue'.Lang::main('colon').$effMV, $_) : $_;
-
- break;
- case 41: // Dispel Immunity
- case 178: // Mod Debuff Resistance
- case 245: // Mod Aura Duration By Dispel
- if ($_ = Lang::game('dt', $effMV))
- $bar = User::isInGroup(U_GROUP_EMPLOYEE) ? sprintf(Util::$dfnString, 'MiscValue'.Lang::main('colon').$effMV, $_) : $_;
-
- break;
- case 44: // Track Creature
- if ($_ = Lang::game('ct', $effMV))
- $bar = User::isInGroup(U_GROUP_EMPLOYEE) ? sprintf(Util::$dfnString, 'MiscValue'.Lang::main('colon').$effMV, $_) : $_;
-
- break;
- case 45: // Track Resource
- if ($_ = Lang::spell('lockType', $effMV))
- $bar = User::isInGroup(U_GROUP_EMPLOYEE) ? sprintf(Util::$dfnString, 'MiscValue'.Lang::main('colon').$effMV, $_) : $_;
-
- break;
- case 75: // Language
- if ($_ = Lang::game('languages', $effMV))
- $bar = User::isInGroup(U_GROUP_EMPLOYEE) ? sprintf(Util::$dfnString, 'MiscValue'.Lang::main('colon').$effMV, $_) : $_;
-
- break;
- case 77: // Mechanic Immunity
- case 117: // Mod Mechanic Resistance
- case 232: // Mod Mechanic Duration
- case 234: // Mod Mechanic Duration (no stack)
- case 255: // Mod Mechanic Damage Taken Pct
- case 276: // Mod Mechanic Damage Done Percent
- if ($_ = Lang::game('me', $effMV))
- $bar = User::isInGroup(U_GROUP_EMPLOYEE) ? sprintf(Util::$dfnString, 'MiscValue'.Lang::main('colon').Util::asHex($effMV), $_) : $_;
-
- break;
- case 147: // Mechanic Immunity Mask
- $_ = [];
- foreach (Lang::game('me') as $k => $str)
- if ($k && ($effMV & (1 << $k - 1)))
- $_[] = $str;
-
- if ($_ = implode(', ', $_))
- $bar = User::isInGroup(U_GROUP_EMPLOYEE) ? sprintf(Util::$dfnString, 'MiscValue'.Lang::main('colon').Util::asHex($effMV), $_) : $_;
-
- break;
- case 10: // Mod Threat
- case 13: // Mod Damage Done
- case 14: // Mod Damage Taken
- case 22: // Mod Resistance
- case 39: // School Immunity
- case 40: // Damage Immunity
- case 50: // Mod Critical Healing Amount
- case 57: // Mod Spell Crit Chance
- case 69: // School Absorb
- case 71: // Mod Spell Crit Chance School
- case 72: // Mod Power Cost School Percent
- case 73: // Mod Power Cost School Flat
- case 74: // Reflect Spell School
- case 79: // Mod Damage Done Pct
- case 81: // Split Damage Pct
- case 83: // Mod Base Resistance
- case 87: // Mod Damage Taken Pct
- case 97: // Mana Shield
- case 101: // Mod Resistance Pct
- case 115: // Mod Healing Taken
- case 118: // Mod Healing Taken Pct
- case 123: // Mod Target Resistance
- case 135: // Mod Healing Done
- case 136: // Mod Healing Done Pct
- case 142: // Mod Base Resistance Pct
- case 143: // Mod Resistance Exclusive
- case 149: // Reduce Pushback
- case 163: // Mod Crit Damage Bonus
- case 174: // Mod Spell Damage Of Stat Percent
- case 182: // Mod Resistance Of Stat Percent
- case 186: // Mod Attacker Spell Hit Chance
- case 194: // Mod Target Absorb School
- case 195: // Mod Target Ability Absorb School
- case 199: // Mod Increases Spell Percent to Hit
- case 229: // Mod AoE Damage Avoidance
- case 271: // Mod Damage Percent Taken Form Caster
- case 310: // Mod Creature AoE Damage Avoidance
- case 237: // Mod Spell Damage Of Attack Power
- case 238: // Mod Spell Healing Of Attack Power
- case 242: // Mod Spell & Healing Power by % of Int
- case 259: // Mod Periodic Healing Taken %
- case 267: // Cancel Aura Buffer at % of Caster Health
- case 269: // Ignore Target Resistance
- case 285: // Mod Attack Power by School Resistance
- case 300: // Share Damage %
- case 301: // Mod Absorb School Healing
- if ($_ = Lang::getMagicSchools($effMV))
- $bar = User::isInGroup(U_GROUP_EMPLOYEE) ? sprintf(Util::$dfnString, 'MiscValue'.Lang::main('colon').Util::asHex($effMV), $_) : $_;
-
- break;
- case 30: // Mod Skill
- case 98: // Mod Skill Value
- if ($n = SkillList::getName($effMV))
- $bar = ' ('.$n.')';
- else
- $bar = Lang::main('colon').Util::ucFirst(Lang::game('skill')).' #'.$effMV;;
-
- break;
- case 107: // Flat Modifier
- case 108: // Pct Modifier
- if ($_ = Lang::spell('spellModOp', $effMV))
- $bar = User::isInGroup(U_GROUP_EMPLOYEE) ? sprintf(Util::$dfnString, 'MiscValue'.Lang::main('colon').$effMV, $_) : $_;
-
- break;
- case 189: // Mod Rating
- case 220: // Combat Rating From Stat
- $_ = [];
- foreach (Lang::spell('combatRating') as $k => $str)
- if ((1 << $k) & $effMV)
- $_[] = $str;
-
- if ($_ = implode(', ', $_))
- $bar = User::isInGroup(U_GROUP_EMPLOYEE) ? sprintf(Util::$dfnString, 'MiscValue'.Lang::main('colon').Util::asHex($effMV), $_) : $_;
-
- break;
- case 168: // Mod Damage Done Versus
- case 59: // Mod Damage Done Versus Creature
- case 102: // Mod Melee Attack Power Versus
- case 131: // Mod Ranged Attack Power Versus
- case 180: // Mod Spell Damage Versus
- $_ = [];
- foreach (Lang::game('ct') as $k => $str)
- if ($k && ($effMV & (1 << $k - 1)))
- $_[] = $str;
-
- if ($_ = implode(', ', $_))
- $bar = User::isInGroup(U_GROUP_EMPLOYEE) ? sprintf(Util::$dfnString, 'MiscValue'.Lang::main('colon').Util::asHex($effMV), $_) : $_;
-
- break;
- case 249: // Convert Rune
- $from = $effMV;
- if ($_ = Lang::spell('powerRunes', $effMV))
- $from = $_;
-
- $to = $effMVB;
- if ($_ = Lang::spell('powerRunes', $effMVB))
- $to = $_;
-
- if (User::isInGroup(U_GROUP_EMPLOYEE))
- $bar = sprintf(Util::$dfnString, 'MiscValue'.Lang::main('colon').$effMV, $from).' => '.sprintf(Util::$dfnString, 'MiscValueB'.Lang::main('colon').$effMVB, $to);
- else
- $bar = $from.' => '.$to;
-
- $effMVB = 0;
-
- break;
- case 78: // Mounted
- case 56: // Transform
- if ($transform = $this->subject->getModelInfo($this->typeId, $i))
- {
- $redButtons[BUTTON_VIEW3D] = ['type' => Type::NPC, 'displayId' => $transform['displayId']];
- $bar = $transform['typeId'] ? ' ('.$transform['displayName'].')' : ' (#0)';
- }
- else
- $bar = Lang::main('colon').Lang::game('npc').' #'.$effMV;;
-
- break;
- case 139: // Force Reaction
- $foo['value'] = sprintf(Util::$dfnString, $foo['value'], Lang::game('rep', $foo['value']));
- // DO NOT BREAK
- case 190: // Mod Faction Reputation Gain
- $n = FactionList::getName($effMV);
- $bar = ' ('.($n ? ''.$n.'' : Util::ucFirst(Lang::game('faction')).' #'.$effMV).')';
- break; // also breaks for 139
- case 293: // Override Spells
- if ($so = DB::Aowow()->selectRow('SELECT spellId1, spellId2, spellId3, spellId4, spellId5 FROM ?_spelloverride WHERE id = ?d', $effMV))
- {
- $buff = [];
- for ($j = 1; $j < 6; $j++)
- {
- if ($x = $so['spellId'.$j])
- {
- $this->extendGlobalData([Type::SPELL => [$x]]);
- $buff[] = '[spell='.$x.']';
- }
- }
- $foo['markup'] = implode(', ', $buff);
- }
- break;
- case 202: // Ignore Combat Result
- case 248: // Mod Combat Result Chance
- $what = '';
- switch ($effMV)
- {
- case 2: // Dodged
- $what = Lang::spell('combatRating', 2);
- break;
- case 3: // Blocked
- $what = Lang::spell('combatRating', 4);
- break;
- case 4: // Parried
- $what = Lang::spell('combatRating', 3);
- break;
- case 0; // Evaded
- case 1: // Missed
- case 5: // Glanced
- case 6: // Crited'ed..ed
- case 7: // Crushed
- case 8: // Regular
- default:
- trigger_error('hitero unused case #'.$effMV.' found for aura 202');
- }
-
- if ($what)
- $bar = User::isInGroup(U_GROUP_EMPLOYEE) ? sprintf(Util::$dfnString, 'MiscValue'.Lang::main('colon').$effMV, $what) : $what;
-
- break;
- case 233: // Change other Humanoid Display
- case 273: // X-Ray
- case 304: // Fake Inebriate
- $bar = ' ('.Lang::game('npc').' #'.$effMV.')';
- if ($n = CreatureList::getName($effMV))
- $bar = ' ('.$n.')';
- }
- $foo['name'] .= strstr($bar, 'href') || strstr($bar, '#') ? $bar : ($bar ? ' ('.$bar.')' : null);
-
- if (in_array($effAura, [174, 220, 182]))
- $foo['name'] .= ' ['.sprintf(Util::$dfnString, 'MiscValueB'.Lang::main('colon').$effMVB, Lang::game('stats', $effMVB)).']';
- else if ($effMVB > 0)
- $foo['name'] .= ' ['.$effMVB.']';
-
- }
- else if ($effAura > 0)
- $foo['name'] .= 'Unknown Aura ('.$effAura.')';
-
- break;
- }
- }
-
- // cases where we dont want 'Value' to be displayed
- if (in_array($effAura, [11, 12, 36, 77]) || in_array($effId, [132]) || empty($foo['value']))
- unset($foo['value']);
- }
-
- unset($foo); // clear reference
-
- return $effects;
- }
-
- private function createAttributesList() : array
- {
- $cbBandageSpell = function()
- {
- return ($this->subject->getField('attributes1') & 0x00004044) && ($this->subject->getField('effect1ImplicitTargetA') == 21);
- };
-
- $cbInverseFlag = function($field, $flag)
- {
- return !($this->subject->getField($field) & $flag);
- };
-
- $cbEquippedWeapon = function ($mask, $useInvType)
- {
- $field = $useInvType ? 'equippedItemInventoryTypeMask' : 'equippedItemSubClassMask';
-
- return ($this->subject->getField('equippedItemClass') == ITEM_CLASS_WEAPON) && ($this->subject->getField($field) & $mask);
- };
-
- $cbSpellstealable = function($field, $flag)
- {
- return !($this->subject->getField($field) & $flag) && ($this->subject->getField('dispelType') == 1);
- };
-
- $list = [];
- $fi = new SpellListFilter();
- foreach (Lang::spell('attributes') as $idx => $_)
- {
- if ($cr = $fi->getGenericFilter($idx))
- {
- if ($cr[0] == FILTER_CR_CALLBACK)
- {
- if (!isset($cr[1]))
- trigger_error('SpellDetailPage::createAttributesList - callback handler '.$cr[1].' not defined for IDX #'.$idx, E_USER_WARNING);
- else if (${$cr[1]}($cr[2] ?? null, $cr[3] ?? null))
- $list[] = $idx;
- }
- else if ($cr[0] == FILTER_CR_FLAG)
- {
- if ($this->subject->getField($cr[1]) & $cr[2])
- $list[] = $idx;
- }
- else
- trigger_error('SpellDetailPage::createAttributesList - unhandled filter case #'.$cr[0].' for IDX #'.$idx, E_USER_WARNING);
- }
- else
- trigger_error('SpellDetailPage::createAttributesList - SpellAttrib IDX #'.$idx.' defined in Lang, but not set as filter', E_USER_WARNING);
- }
-
- return $list;
- }
-}
-
-
-
-?>
diff --git a/pages/talent.php b/pages/talent.php
deleted file mode 100644
index 79df675a..00000000
--- a/pages/talent.php
+++ /dev/null
@@ -1,59 +0,0 @@
-isPetCalc = $pageCall == 'petcalc';
- $this->name = $this->isPetCalc ? Lang::main('petCalc') : Lang::main('talentCalc');
- }
-
- protected function generateContent()
- {
- // add conditional js & css
- $this->addScript(
- [JS_FILE, ($this->isPetCalc ? '?data=pet-talents.pets' : '?data=glyphs').'&locale='.User::$localeId.'&t='.$_SESSION['dataKey']],
- [JS_FILE, $this->isPetCalc ? 'petcalc.js' : 'talent.js'],
- );
-
- if ($this->isPetCalc)
- $this->addScript(
- [JS_FILE, 'swfobject.js'],
- [CSS_FILE, 'petcalc.css']
- );
-
- $this->tcType = $this->isPetCalc ? 'pc' : 'tc';
- }
-
- protected function generateTitle()
- {
- array_unshift($this->title, $this->name);
- }
-
- protected function generatePath()
- {
- $this->path[] = $this->isPetCalc ? 2 : 0;
- }
-}
-
-?>
diff --git a/pages/title.php b/pages/title.php
deleted file mode 100644
index a363cc2d..00000000
--- a/pages/title.php
+++ /dev/null
@@ -1,138 +0,0 @@
-typeId = intVal($id);
-
- $this->subject = new TitleList(array(['id', $this->typeId]));
- if ($this->subject->error)
- $this->notFound(Lang::game('title'), Lang::title('notFound'));
-
- $this->name = $this->subject->getHtmlizedName();
- $this->nameFixed = Util::ucFirst(trim(strtr($this->subject->getField('male', true), ['%s' => '', ',' => ''])));
- }
-
- protected function generatePath()
- {
- $this->path[] = $this->subject->getField('category');
- }
-
- protected function generateTitle()
- {
- array_unshift($this->title, $this->nameFixed, Util::ucFirst(Lang::game('title')));
- }
-
- protected function generateContent()
- {
- /***********/
- /* Infobox */
- /***********/
-
- $infobox = Lang::getInfoBoxForFlags($this->subject->getField('cuFlags'));
-
- if ($this->subject->getField('side') == SIDE_ALLIANCE)
- $infobox[] = Lang::main('side').Lang::main('colon').'[span class=icon-alliance]'.Lang::game('si', SIDE_ALLIANCE).'[/span]';
- else if ($this->subject->getField('side') == SIDE_HORDE)
- $infobox[] = Lang::main('side').Lang::main('colon').'[span class=icon-horde]'.Lang::game('si', SIDE_HORDE).'[/span]';
- else
- $infobox[] = Lang::main('side').Lang::main('colon').Lang::game('si', SIDE_BOTH);
-
- if ($g = $this->subject->getField('gender'))
- $infobox[] = Lang::main('gender').Lang::main('colon').'[span class=icon-'.($g == 2 ? 'female' : 'male').']'.Lang::main('sex', $g).'[/span]';
-
- if ($eId = $this->subject->getField('eventId'))
- {
- $this->extendGlobalIds(Type::WORLDEVENT, $eId);
- $infobox[] = Lang::game('eventShort').Lang::main('colon').'[event='.$eId.']';
- }
-
- /****************/
- /* Main Content */
- /****************/
-
- $this->infobox = $infobox ? '[ul][li]'.implode('[/li][li]', $infobox).'[/li][/ul]' : null;
- $this->expansion = Util::$expansionString[$this->subject->getField('expansion')];
- $this->redButtons = array(
- BUTTON_WOWHEAD => true,
- BUTTON_LINKS => ['type' => $this->type, 'typeId' => $this->typeId]
- );
-
- // factionchange-equivalent
- if ($pendant = DB::World()->selectCell('SELECT IF(horde_id = ?d, alliance_id, -horde_id) FROM player_factionchange_titles WHERE alliance_id = ?d OR horde_id = ?d', $this->typeId, $this->typeId, $this->typeId))
- {
- $altTitle = new TitleList(array(['id', abs($pendant)]));
- if (!$altTitle->error)
- {
- $this->transfer = sprintf(
- Lang::title('_transfer'),
- $altTitle->id,
- $altTitle->getHtmlizedName(),
- $pendant > 0 ? 'alliance' : 'horde',
- $pendant > 0 ? Lang::game('si', 1) : Lang::game('si', 2)
- );
- }
- }
-
- /**************/
- /* Extra Tabs */
- /**************/
-
- // tab: quest source
- $quests = new QuestList(array(['rewardTitleId', $this->typeId]));
- if (!$quests->error)
- {
- $this->extendGlobalData($quests->getJSGlobals(GLOBALINFO_REWARDS));
-
- $this->lvTabs[] = ['quest', array(
- 'data' => array_values($quests->getListviewData()),
- 'id' => 'reward-from-quest',
- 'name' => '$LANG.tab_rewardfrom',
- 'hiddenCols' => ['experience', 'money'],
- 'visibleCols' => ['category']
- )];
- }
-
- // tab: achievement source
- if ($aIds = DB::World()->selectCol('SELECT ID FROM achievement_reward WHERE TitleA = ?d OR TitleH = ?d', $this->typeId, $this->typeId))
- {
- $acvs = new AchievementList(array(['id', $aIds]));
- if (!$acvs->error)
- {
- $this->extendGlobalData($acvs->getJSGlobals());
-
- $this->lvTabs[] = ['achievement', array(
- 'data' => array_values($acvs->getListviewData()),
- 'id' => 'reward-from-achievement',
- 'name' => '$LANG.tab_rewardfrom',
- 'visibleCols' => ['category'],
- 'sort' => ['reqlevel', 'name']
- )];
- }
- }
-
- // tab: criteria of (to be added by TC)
- }
-}
-
-?>
diff --git a/pages/titles.php b/pages/titles.php
deleted file mode 100644
index fb0ce68f..00000000
--- a/pages/titles.php
+++ /dev/null
@@ -1,69 +0,0 @@
-getCategoryFromUrl($pageParam);;
-
- parent::__construct($pageCall, $pageParam);
-
- $this->name = Util::ucFirst(Lang::game('titles'));
- }
-
- protected function generateContent()
- {
- $conditions = [];
-
- if (!User::isInGroup(U_GROUP_EMPLOYEE)) // hide unused titles
- $conditions[] = [['cuFlags', CUSTOM_EXCLUDE_FOR_LISTVIEW, '&'], 0];
-
- if ($this->category)
- $conditions[] = ['category', $this->category[0]];
-
- $tabData = ['data' => []];
- $titles = new TitleList($conditions);
- if (!$titles->error)
- {
- $tabData['data'] = array_values($titles->getListviewData());
-
- if ($titles->hasDiffFields(['category']))
- $tabData['visibleCols'] = ['category'];
-
- if (!$titles->hasAnySource())
- $tabData['hiddenCols'] = ['source'];
- }
-
- $this->lvTabs[] = ['title', $tabData];
- }
-
- protected function generateTitle()
- {
- array_unshift($this->title, Util::ucFirst(Lang::game('titles')));
- if ($this->category)
- array_unshift($this->title, Lang::title('cat', $this->category[0]));
- }
-
- protected function generatePath()
- {
- if ($this->category)
- $this->path[] = $this->category[0]; // should be only one parameter anyway
- }
-}
-
-?>
diff --git a/pages/user.php b/pages/user.php
deleted file mode 100644
index 125454f5..00000000
--- a/pages/user.php
+++ /dev/null
@@ -1,263 +0,0 @@
-selectRow('SELECT a.id, a.user, a.displayName, a.consecutiveVisits, a.userGroups, a.avatar, a.title, a.description, a.joinDate, a.prevLogin, IFNULL(SUM(ar.amount), 0) AS sumRep FROM ?_account a LEFT JOIN ?_account_reputation ar ON a.id = ar.userId WHERE a.user = ? GROUP BY a.id', $pageParam))
- $this->user = $user;
- else
- $this->notFound(sprintf(Lang::user('notFound'), $pageParam));
- }
- else if (User::$id)
- {
- header('Location: ?user='.User::$displayName, true, 302);
- die();
- }
- else
- $this->forwardToSignIn('user');
- }
-
- protected function generateContent()
- {
- if (!$this->user) // shouldn't happen .. but did
- return;
-
-
- /***********/
- /* Infobox */
- /***********/
-
- $infobox = $contrib = $groups = [];
- foreach (Lang::account('groups') as $idx => $key)
- if ($idx >= 0 && $this->user['userGroups'] & (1 << $idx))
- $groups[] = (!fMod(count($groups) + 1, 3) ? '[br]' : null).Lang::account('groups', $idx);
-
- $infobox[] = Lang::user('joinDate'). Lang::main('colon').'[tooltip name=joinDate]'. date('l, G:i:s', $this->user['joinDate']). '[/tooltip][span class=tip tooltip=joinDate]'. date(Lang::main('dateFmtShort'), $this->user['joinDate']). '[/span]';
- $infobox[] = Lang::user('lastLogin').Lang::main('colon').'[tooltip name=lastLogin]'.date('l, G:i:s', $this->user['prevLogin']).'[/tooltip][span class=tip tooltip=lastLogin]'.date(Lang::main('dateFmtShort'), $this->user['prevLogin']).'[/span]';
- $infobox[] = Lang::user('userGroups').Lang::main('colon').($groups ? implode(', ', $groups) : Lang::account('groups', -1));
- $infobox[] = Lang::user('consecVisits').Lang::main('colon').$this->user['consecutiveVisits'];
- $infobox[] = Util::ucFirst(Lang::main('siteRep')).Lang::main('colon').Lang::nf($this->user['sumRep']);
-
- // contrib -> [url=http://www.wowhead.com/client]Data uploads: n [small]([tooltip=tooltip_totaldatauploads]xx.y MB[/tooltip])[/small][/url]
-
- $co = DB::Aowow()->selectRow(
- 'SELECT COUNT(DISTINCT c.id) AS sum, SUM(IFNULL(ur.value, 0)) AS nRates FROM ?_comments c LEFT JOIN ?_user_ratings ur ON ur.entry = c.id AND ur.type = ?d AND ur.userId <> 0 WHERE c.replyTo = 0 AND c.userId = ?d',
- RATING_COMMENT,
- $this->user['id']
- );
- if ($co['sum'])
- $contrib[] = Lang::user('comments').Lang::main('colon').$co['sum'].($co['nRates'] ? ' [small]([tooltip=tooltip_totalratings]'.$co['nRates'].'[/tooltip])[/small]' : null);
-
- $ss = DB::Aowow()->selectRow('SELECT COUNT(*) AS sum, SUM(IF(status & ?d, 1, 0)) AS nSticky, SUM(IF(status & ?d, 0, 1)) AS nPending FROM ?_screenshots WHERE userIdOwner = ?d AND (status & ?d) = 0',
- CC_FLAG_STICKY,
- CC_FLAG_APPROVED,
- $this->user['id'],
- CC_FLAG_DELETED
- );
- if ($ss['sum'])
- {
- $buff = [];
- if ($ss['nSticky'] || $ss['nPending'])
- {
- if ($normal = ($ss['sum'] - $ss['nSticky'] - $ss['nPending']))
- $buff[] = '[tooltip=tooltip_normal]'.$normal.'[/tooltip]';
-
- if ($ss['nSticky'])
- $buff[] = '[tooltip=tooltip_sticky]'.$ss['nSticky'].'[/tooltip]';
-
- if ($ss['nPending'])
- $buff[] = '[tooltip=tooltip_pending]'.$ss['nPending'].'[/tooltip]';
- }
-
- $contrib[] = Lang::user('screenshots').Lang::main('colon').$ss['sum'].($buff ? ' [small]('.implode(' + ', $buff).')[/small]' : null);
- }
-
- $vi = DB::Aowow()->selectRow('SELECT COUNT(id) AS sum, SUM(IF(status & ?d, 1, 0)) AS nSticky, SUM(IF(status & ?d, 0, 1)) AS nPending FROM ?_videos WHERE userIdOwner = ?d AND (status & ?d) = 0',
- CC_FLAG_STICKY,
- CC_FLAG_APPROVED,
- $this->user['id'],
- CC_FLAG_DELETED
- );
- if ($vi['sum'])
- {
- $buff = [];
- if ($vi['nSticky'] || $vi['nPending'])
- {
- if ($normal = ($vi['sum'] - $vi['nSticky'] - $vi['nPending']))
- $buff[] = '[tooltip=tooltip_normal]'.$normal.'[/tooltip]';
-
- if ($vi['nSticky'])
- $buff[] = '[tooltip=tooltip_sticky]'.$vi['nSticky'].'[/tooltip]';
-
- if ($vi['nPending'])
- $buff[] = '[tooltip=tooltip_pending]'.$vi['nPending'].'[/tooltip]';
- }
-
- $contrib[] = Lang::user('videos').Lang::main('colon').$vi['sum'].($buff ? ' [small]('.implode(' + ', $buff).')[/small]' : null);
- }
-
- // contrib -> Forum posts: 5769 [small]([tooltip=topics]579[/tooltip] + [tooltip=replies]5190[/tooltip])[/small]
-
- $this->infobox = '[ul][li]'.implode('[/li][li]', $infobox).'[/li][/ul]';
-
- if ($contrib)
- $this->contributions = '[ul][li]'.implode('[/li][li]', $contrib).'[/li][/ul]';
-
-
- /****************/
- /* Main Content */
- /****************/
-
- $this->name = $this->user['title'] ? $this->user['displayName'].' <'.$this->user['title'].'>' : sprintf(Lang::user('profileTitle'), $this->user['displayName']);
-
- /**************/
- /* Extra Tabs */
- /**************/
-
- $this->lvTabs = [];
- $this->forceTabs = true;
-
- // [unused] Site Achievements
-
- // Reputation changelog (params only for comment-events)
- if ($repData = DB::Aowow()->select('SELECT action, amount, date AS \'when\', IF(action IN (3, 4, 5), sourceA, 0) AS param FROM ?_account_reputation WHERE userId = ?d', $this->user['id']))
- {
- foreach ($repData as &$r)
- $r['when'] = date(Util::$dateFormatInternal, $r['when']);
-
- $this->lvTabs[] = ['reputationhistory', ['data' => $repData]];
- }
-
- // Comments
- if ($_ = CommunityContent::getCommentPreviews(['user' => $this->user['id'], 'replies' => false], $nFound))
- {
- $tabData = array(
- 'data' => $_,
- 'hiddenCols' => ['author'],
- 'onBeforeCreate' => '$Listview.funcBox.beforeUserComments',
- '_totalCount' => $nFound
- );
-
- if ($nFound > CFG_SQL_LIMIT_DEFAULT)
- {
- $tabData['name'] = '$LANG.tab_latestcomments';
- $tabData['note'] = '$$WH.sprintf(LANG.lvnote_usercomments, '.$nFound.')';
- }
-
- $this->lvTabs[] = ['commentpreview', $tabData];
- }
-
- // Comment Replies
- if ($_ = CommunityContent::getCommentPreviews(['user' => $this->user['id'], 'replies' => true], $nFound))
- {
- $tabData = array(
- 'data' => $_,
- 'hiddenCols' => ['author'],
- 'onBeforeCreate' => '$Listview.funcBox.beforeUserComments',
- '_totalCount' => $nFound
- );
-
- if ($nFound > CFG_SQL_LIMIT_DEFAULT)
- {
- $tabData['name'] = '$LANG.tab_latestreplies';
- $tabData['note'] = '$$WH.sprintf(LANG.lvnote_userreplies, '.$nFound.')';
- }
-
- $this->lvTabs[] = ['replypreview', $tabData];
- }
-
- // Screenshots
- if ($_ = CommunityContent::getScreenshots(-$this->user['id'], 0, $nFound))
- {
- $tabData = array(
- 'data' => $_,
- '_totalCount' => $nFound
- );
-
- if ($nFound > CFG_SQL_LIMIT_DEFAULT)
- {
- $tabData['name'] = '$LANG.tab_latestscreenshots';
- $tabData['note'] = '$$WH.sprintf(LANG.lvnote_userscreenshots, '.$nFound.')';
- }
-
- $this->lvTabs[] = ['screenshot', $tabData];
- }
-
- // Videos
- if ($_ = CommunityContent::getVideos(-$this->user['id'], 0, $nFound))
- {
- $tabData = array(
- 'data' => $_,
- '_totalCount' => $nFound
- );
-
- if ($nFound > CFG_SQL_LIMIT_DEFAULT)
- {
- $tabData['name'] = '$LANG.tab_latestvideos';
- $tabData['note'] = '$$WH.sprintf(LANG.lvnote_uservideos, '.$nFound.')';
- }
-
- $this->lvTabs[] = ['video', $tabData];
- }
-
- // forum -> latest topics [unused]
-
- // forum -> latest replies [unused]
-
- $conditions = array(
- ['OR', ['cuFlags', PROFILER_CU_PUBLISHED, '&'], ['ap.extraFlags', PROFILER_CU_PUBLISHED, '&']],
- [['cuFlags', PROFILER_CU_DELETED, '&'], 0],
- ['OR', ['user', $this->user['id']], ['ap.accountId', $this->user['id']]]
- );
-
- if (User::isInGroup(U_GROUP_ADMIN | U_GROUP_BUREAU))
- $conditions = array_slice($conditions, 2);
- else if (User::$id == $this->user['id'])
- array_shift($conditions);
-
- $profiles = new LocalProfileList($conditions);
- if (!$profiles->error)
- {
- $this->addScript([JS_FILE, '?data=weight-presets&locale='.User::$localeId.'&t='.$_SESSION['dataKey']]);
-
- // Characters
- if ($chars = $profiles->getListviewData(PROFILEINFO_CHARACTER | PROFILEINFO_USER))
- $this->user['characterData'] = $chars;
-
- // Profiles
- if ($prof = $profiles->getListviewData(PROFILEINFO_PROFILE | PROFILEINFO_USER))
- $this->user['profileData'] = $prof;
- }
- }
-
- protected function generateTitle()
- {
- if (!$this->user) // shouldn't happen .. but did
- return;
-
- array_unshift($this->title, sprintf(Lang::user('profileTitle'), $this->user['displayName']));
- }
-
- protected function generatePath() { }
-}
-
-?>
diff --git a/pages/utility.php b/pages/utility.php
deleted file mode 100644
index fd9f1ca7..00000000
--- a/pages/utility.php
+++ /dev/null
@@ -1,317 +0,0 @@
- 'latest-videos', 12 => 'most-comments', 13 => 'missing-screenshots'
- );
-
- protected $_get = ['rss' => ['filter' => FILTER_CALLBACK, 'options' => 'GenericPage::checkEmptySet']];
-
- private $page = '';
- private $rss = false;
- private $feedData = [];
-
- public function __construct($pageCall, $pageParam)
- {
- $this->getCategoryFromUrl($pageParam);
-
- parent::__construct($pageCall, $pageParam);
-
- $this->page = $pageCall;
- $this->rss = $this->_get['rss'];
-
- if ($this->page != 'random')
- $this->name = Lang::main('utilities', array_search($pageCall, $this->validPages));
-
- if ($this->page == 'most-comments')
- {
- if ($this->category && in_array($this->category[0], [7, 30]))
- $this->name .= Lang::main('colon') . sprintf(Lang::main('mostComments', 1), $this->category[0]);
- else
- $this->name .= Lang::main('colon') . Lang::main('mostComments', 0);
- }
-
- $this->lvTabs = [];
- }
-
- public function display(string $override = '') : void
- {
- if ($this->rss) // this should not be cached
- {
- header(MIME_TYPE_RSS);
- die($this->generateRSS());
- }
- else
- parent::display($override);
- }
-
- protected function generateContent()
- {
- /****************/
- /* Main Content */
- /****************/
-
- if (in_array(array_search($this->page, $this->validPages), [2, 3, 11, 12]))
- $this->h1Links = '';
-
- switch ($this->page)
- {
- case 'random':
- $type = array_rand(Type::getClassesFor(Type::FLAG_RANDOM_SEARCHABLE));
- $typeId = (Type::newList($type, null))?->getRandomId();
-
- header('Location: ?'.Type::getFileString($type).'='.$typeId, true, 302);
- die();
- case 'latest-comments': // rss
- $data = CommunityContent::getCommentPreviews(dateFmt: false);
-
- if ($this->rss)
- {
- foreach ($data as $d)
- {
- // todo (low): preview should be html-formated
- $this->feedData[] = array(
- 'title' => [true, [], Lang::typeName($d['type']).Lang::main('colon').htmlentities($d['subject'])],
- 'link' => [false, [], HOST_URL.'/?go-to-comment&id='.$d['id']],
- 'description' => [true, [], htmlentities($d['preview'])."
".Lang::main('byUser', [$d['user'], '']) . Util::formatTimeDiff($d['date'], true)],
- 'pubDate' => [false, [], date(DATE_RSS, $d['date'])],
- 'guid' => [false, [], HOST_URL.'/?go-to-comment&id='.$d['id']]
- // 'domain' => [false, [], null]
- );
- }
- }
- else
- $this->lvTabs[] = ['commentpreview', ['data' => array_values($data)]];
-
- break;
- case 'latest-screenshots': // rss
- $data = CommunityContent::getScreenshots(dateFmt: false);
-
- if ($this->rss)
- {
- foreach ($data as $d)
- {
- $desc = '
';
- if ($d['caption'])
- $desc .= '
'.$d['caption'];
- $desc .= "
".Lang::main('byUser', [$d['user'], '']) . Util::formatTimeDiff($d['date'], true);
-
- // enclosure/length => filesize('static/uploads/screenshots/thumb/'.$d['id'].'.jpg') .. always set to this placeholder value though
- $this->feedData[] = array(
- 'title' => [true, [], Lang::typeName($d['type']).Lang::main('colon').htmlentities($d['subject'])],
- 'link' => [false, [], HOST_URL.'/?'.Type::getFileString($d['type']).'='.$d['typeId'].'#screenshots:id='.$d['id']],
- 'description' => [true, [], $desc],
- 'pubDate' => [false, [], date(DATE_RSS, $d['date'])],
- 'enclosure' => [false, ['url' => STATIC_URL.'/uploads/screenshots/thumb/'.$d['id'].'.jpg', 'length' => 12345, 'type' => 'image/jpeg'], null],
- 'guid' => [false, [], HOST_URL.'/?'.Type::getFileString($d['type']).'='.$d['typeId'].'#screenshots:id='.$d['id']],
- // 'domain' => [false, [], live|ptr]
- );
- }
- }
- else
- $this->lvTabs[] = ['screenshot', ['data' => array_values($data)]];
-
- break;
- case 'latest-videos': // rss
- $data = CommunityContent::getVideos(dateFmt: false);
-
- if ($this->rss)
- {
- foreach ($data as $d)
- {
- $desc = '
';
- if ($d['caption'])
- $desc .= '
'.$d['caption'];
- $desc .= "
".Lang::main('byUser', [$d['user'], '']) . Util::formatTimeDiff($d['date'], true);
-
- // is enclosure/length .. is this even relevant..?
- $this->feedData[] = array(
- 'title' => [true, [], Lang::typeName($d['type']).Lang::main('colon').htmlentities($d['subject'])],
- 'link' => [false, [], HOST_URL.'/?'.Type::getFileString($d['type']).'='.$d['typeId'].'#videos:id='.$d['id']],
- 'description' => [true, [], $desc],
- 'pubDate' => [false, [], date(DATE_RSS, $d['date'])],
- 'enclosure' => [false, ['url' => '//i3.ytimg.com/vi/'.$d['videoId'].'/default.jpg', 'length' => 12345, 'type' => 'image/jpeg'], null],
- 'guid' => [false, [], HOST_URL.'/?'.Type::getFileString($d['type']).'='.$d['typeId'].'#videos:id='.$d['id']],
- // 'domain' => [false, [], live|ptr]
- );
- }
- }
- else
- $this->lvTabs[] = ['video', ['data' => array_values($data)]];
-
- break;
- case 'unrated-comments':
-
- // EXPLAIN SELECT ac.* FROM aowow_comments ac LEFT JOIN aowow_comments_rates acr ON acr.commentId = ac.id AND acr.userId <> 0 WHERE acr.commentId IS NULL;
- if ($_ = CommunityContent::getCommentPreviews(['user' => User::$id, 'replies' => false], $nFound))
- {
- $tabData = array(
- 'data' => $_,
- 'onBeforeCreate' => '$Listview.funcBox.beforeUserComments',
- '_totalCount' => $nFound
- );
-
- if ($nFound > CFG_SQL_LIMIT_DEFAULT)
- {
- $tabData['name'] = '$LANG.tab_latestcomments';
- $tabData['note'] = '$$WH.sprintf(LANG.lvnote_usercomments, '.$nFound.')';
- }
-
- $this->lvTabs[] = ['commentpreview', $tabData];
- }
-
-
- $this->lvTabs[] = ['commentpreview', ['data' => []]];
- break;
- case 'missing-screenshots':
- // limit to 200 entries each (it generates faster, consumes less memory and should be enough options)
- $cnd = [[['cuFlags', CUSTOM_HAS_SCREENSHOT, '&'], 0], 200];
- if (!User::isInGroup(U_GROUP_EMPLOYEE))
- $cnd[] = [['cuFlags', CUSTOM_EXCLUDE_FOR_LISTVIEW, '&'], 0];
-
-
- foreach (Type::getClassesFor(Type::FLAG_NONE, 'contribute', CONTRIBUTE_SS) as $type => $classStr)
- {
- $typeObj = new $classStr($cnd);
- if (!$typeObj->error)
- {
- $this->extendGlobalData($typeObj->getJSGlobals(GLOBALINFO_ANY));
- $this->lvTabs[] = [$typeObj::$brickFile, ['data' => array_values($typeObj->getListviewData())]];
- }
- }
- break;
- case 'most-comments': // rss
- if ($this->category && !in_array($this->category[0], [1, 7, 30]))
- header('Location: ?most-comments=1'.($this->rss ? '&rss' : null), true, 302);
-
- $tabBase = array(
- 'extraCols' => ["\$Listview.funcBox.createSimpleCol('ncomments', 'tab_comments', '10%', 'ncomments')"],
- 'sort' => ['-ncomments']
- );
-
- foreach (Type::getClassesFor() as $type => $classStr)
- {
- $comments = DB::Aowow()->selectCol('
- SELECT `typeId` AS ARRAY_KEY, count(1) FROM ?_comments
- WHERE `replyTo` = 0 AND (`flags` & ?d) = 0 AND `type`= ?d AND `date` > (UNIX_TIMESTAMP() - ?d)
- GROUP BY `type`, `typeId`
- LIMIT 100',
- CC_FLAG_DELETED,
- $type,
- (isset($this->category[0]) ? $this->category[0] : 1) * DAY
- );
- if (!$comments)
- continue;
-
- $typeClass = new $classStr(array(['id', array_keys($comments)]));
- if (!$typeClass->error)
- {
- $data = $typeClass->getListviewData();
-
- if ($this->rss)
- {
- foreach ($data as $typeId => &$d)
- {
- $this->feedData[] = array(
- 'title' => [true, [], htmlentities(Type::getFileString($type) == 'item' ? mb_substr($d['name'], 1) : $d['name'])],
- 'type' => [false, [], Type::getFileString($type)],
- 'link' => [false, [], HOST_URL.'/?'.Type::getFileString($type).'='.$d['id']],
- 'ncomments' => [false, [], $comments[$typeId]]
- );
- }
- }
- else
- {
- foreach ($data as $typeId => &$d)
- $d['ncomments'] = $comments[$typeId];
-
- $this->extendGlobalData($typeClass->getJSGlobals(GLOBALINFO_ANY));
- $this->lvTabs[] = [$typeClass::$brickFile, array_merge($tabBase, ['data' => array_values($data)])];
- }
- }
- }
-
- break;
- }
-
- // found nothing => set empty content
- // tpl: commentpreview - anything, doesn't matter what
- if (!$this->lvTabs && !$this->rss)
- $this->lvTabs[] = ['commentpreview', ['data' => []]];
- }
-
- protected function generateRSS()
- {
- $this->generateContent();
-
- $root = new SimpleXML('');
- $root->addAttribute('version', '2.0');
-
- $channel = $root->addChild('channel');
-
- $channel->addChild('title', CFG_NAME_SHORT.' - '.$this->name);
- $channel->addChild('link', HOST_URL.'/?'.$this->page . ($this->category ? '='.$this->category[0] : null));
- $channel->addChild('description', CFG_NAME);
- $channel->addChild('language', implode('-', str_split(User::$localeString, 2)));
- $channel->addChild('ttl', CFG_TTL_RSS);
- $channel->addChild('lastBuildDate', date(DATE_RSS));
-
- foreach ($this->feedData as $row)
- {
- $item = $channel->addChild('item');
-
- foreach ($row as $key => [$isCData, $attrib, $text])
- {
- if ($isCData && $text)
- $child = $item->addChild($key)->addCData($text);
- else
- $child = $item->addChild($key, $text);
-
- foreach ($attrib as $k => $v)
- $child->addAttribute($k, $v);
- }
- }
-
- return $root->asXML();
- }
-
- protected function generateTitle()
- {
- if ($this->page == 'most-comments')
- {
- if ($this->category && in_array($this->category[0], [7, 30]))
- array_unshift($this->title, sprintf(Lang::main('mostComments', 1), $this->category[0]));
- else
- array_unshift($this->title, Lang::main('mostComments', 0));
- }
-
- array_unshift($this->title, $this->name);
- }
-
- protected function generatePath()
- {
- $this->path[] = array_search($this->page, $this->validPages);
-
- if ($this->page == 'most-comments')
- {
- if ($this->category && in_array($this->category[0], [7, 30]))
- $this->path[] = $this->category[0];
- else
- $this->path[] = 1;
- }
- }
-}
-
-?>
diff --git a/pages/zone.php b/pages/zone.php
deleted file mode 100644
index 22e06102..00000000
--- a/pages/zone.php
+++ /dev/null
@@ -1,828 +0,0 @@
-typeId = intVal($id);
-
- parent::__construct($pageCall, $id);
-
- $this->subject = new ZoneList(array(['id', $this->typeId]));
- if ($this->subject->error)
- $this->notFound(Lang::game('zone'), Lang::zone('notFound'));
-
- $this->name = $this->subject->getField('name', true);
- }
-
- protected function generateContent()
- {
- $this->addScript([JS_FILE, '?data=zones&locale='.User::$localeId.'&t='.$_SESSION['dataKey']]);
-
- /***********/
- /* Infobox */
- /***********/
-
- $infobox = Lang::getInfoBoxForFlags($this->subject->getField('cuFlags'));
-
- // City
- if ($this->subject->getField('flags') & 0x8 && !$this->subject->getField('parentArea'))
- $infobox[] = Lang::zone('city');
-
- // Auto repop
- if ($this->subject->getField('flags') & 0x1000 && !$this->subject->getField('parentArea'))
- $infobox[] = Lang::zone('autoRez');
-
- // Level
- if ($_ = $this->subject->getField('levelMin'))
- {
- if ($_ < $this->subject->getField('levelMax'))
- $_ .= ' - '.$this->subject->getField('levelMax');
-
- $infobox[] = Lang::game('level').Lang::main('colon').$_;
- }
-
- // required Level
- if ($_ = $this->subject->getField('levelReq'))
- {
- if ($__ = $this->subject->getField('levelReqLFG'))
- $buff = sprintf(Lang::zone('reqLevels'), $_, $__);
- else
- $buff = Lang::main('_reqLevel').Lang::main('colon').$_;
-
- $infobox[] = $buff;
- }
-
- // Territory
- $_ = $this->subject->getField('faction');
- $__ = '%s';
- if ($_ == 0)
- $__ = '[span class=icon-alliance]%s[/span]';
- else if ($_ == 1)
- $__ = '[span class=icon-horde]%s[/span]';
- else if ($_ == 4)
- $__ = '[span class=icon-ffa]%s[/span]';
-
- $infobox[] = Lang::zone('territory').Lang::main('colon').sprintf($__, Lang::zone('territories', $_));
-
- // Instance Type
- $infobox[] = Lang::zone('instanceType').Lang::main('colon').'[span class=icon-instance'.$this->subject->getField('type').']'.Lang::zone('instanceTypes', $this->subject->getField('type')).'[/span]';
-
- // Heroic mode
- if ($_ = $this->subject->getField('levelHeroic'))
- $infobox[] = '[icon preset=heroic]'.sprintf(Lang::zone('hcAvailable'), $_).'[/icon]';
-
- // number of players
- if ($_ = $this->subject->getField('maxPlayer'))
- $infobox[] = Lang::zone('numPlayers').Lang::main('colon').($_ == -2 ? '10/25' : $_);
-
- // Attunement Quest/Achievements & Keys
- if ($attmnt = $this->subject->getField('attunes'))
- {
- foreach ($attmnt as $type => $ids)
- {
- $this->extendGlobalIds($type, ...array_map('abs', $ids));
- foreach ($ids as $id)
- {
- if ($type == Type::ITEM)
- $infobox[] = Lang::zone('key', (int)($id < 0)).Lang::main('colon').'[item='.abs($id).']';
- else
- $infobox[] = Lang::zone('attunement', (int)($id < 0)).Lang::main('colon').'['.Type::getFileString($type).'='.abs($id).']';
- }
- }
- }
-
- // Instances
- if ($_ = DB::Aowow()->selectCol('SELECT id FROM ?_zones WHERE parentAreaId = ?d AND (flags & ?d) = 0', $this->typeId, CUSTOM_EXCLUDE_FOR_LISTVIEW))
- {
- $this->extendGlobalIds(Type::ZONE, ...$_);
- $infobox[] = Lang::maps('Instances').Lang::main('colon')."\n[zone=".implode("], \n[zone=", $_).']';
- }
-
- // location (if instance)
- if ($pa = $this->subject->getField('parentAreaId'))
- {
- $paO = new ZoneList(array(['id', $pa]));
- if (!$paO->error)
- {
- $pins = str_pad($this->subject->getField('parentX') * 10, 3, '0', STR_PAD_LEFT) . str_pad($this->subject->getField('parentY') * 10, 3, '0', STR_PAD_LEFT);
- $infobox[] = Lang::zone('location').Lang::main('colon').'[lightbox=map zone='.$pa.' pins='.$pins.']'.$paO->getField('name', true).'[/lightbox]';
- }
- }
-
-/* has to be defined in an article, i think
-
- // faction(s) / Reputation Hub / Raid Faction
- // [li]Raid faction: [faction=1156][/li] || [li]Factions: [faction=1156]/[faction=1156][/li]
-
- // final boss
- // [li]Final boss: [icon preset=boss][npc=37226][/icon][/li]
-*/
-
- /****************/
- /* Main Content */
- /****************/
-
- $addToSOM = function ($what, $entry) use (&$som)
- {
- // entry always contains: type, id, name, level, coords[]
- if (!isset($som[$what][$entry['name']])) // not found yet
- $som[$what][$entry['id']][] = $entry;
- else // found .. something..
- {
- // check for identical floors
- foreach ($som[$what][$entry['id']] as &$byFloor)
- {
- if ($byFloor['level'] != $entry['level'])
- continue;
-
- // found existing floor, ammending coords
- $byFloor['coords'][] = $entry['coords'][0];
- return;
- }
-
- // floor not used yet, create it
- $som[$what][$entry['id']][] = $entry;
- }
- };
-
- if ($_ = $this->subject->getField('parentArea'))
- {
- $this->extraText = sprintf(Lang::zone('zonePartOf'), $_);
- $this->extendGlobalIds(Type::ZONE, $_);
- }
-
- // we cannot fetch spawns via lists. lists are grouped by entry
- $oSpawns = DB::Aowow()->select('SELECT * FROM ?_spawns WHERE areaId = ?d AND type = ?d', $this->typeId, Type::OBJECT);
- $cSpawns = DB::Aowow()->select('SELECT * FROM ?_spawns WHERE areaId = ?d AND type = ?d', $this->typeId, Type::NPC);
- $aSpawns = User::isInGroup(U_GROUP_STAFF) ? DB::Aowow()->select('SELECT * FROM ?_spawns WHERE areaId = ?d AND type = ?d', $this->typeId, Type::AREATRIGGER) : [];
-
- $conditions = [CFG_SQL_LIMIT_NONE, ['s.areaId', $this->typeId]];
- if (!User::isInGroup(U_GROUP_STAFF))
- $conditions[] = [['cuFlags', CUSTOM_EXCLUDE_FOR_LISTVIEW, '&'], 0];
-
- $objectSpawns = new GameObjectList($conditions);
- $creatureSpawns = new CreatureList($conditions);
- $atSpawns = new AreaTriggerList($conditions);
-
- $questsLV = $rewardsLV = [];
-
- // see if we can actually display a map
- $hasMap = file_exists('static/images/wow/maps/'.Util::$localeStrings[User::$localeId].'/normal/'.$this->typeId.'.jpg');
- if (!$hasMap) // try multilayered
- $hasMap = file_exists('static/images/wow/maps/'.Util::$localeStrings[User::$localeId].'/normal/'.$this->typeId.'-1.jpg');
- if (!$hasMap) // try english fallback
- $hasMap = file_exists('static/images/wow/maps/enus/normal/'.$this->typeId.'.jpg');
- if (!$hasMap) // try english fallback, multilayered
- $hasMap = file_exists('static/images/wow/maps/enus/normal/'.$this->typeId.'-1.jpg');
-
- if ($hasMap)
- {
- $som = [];
- foreach ($oSpawns as $spawn)
- {
- $tpl = $objectSpawns->getEntry($spawn['typeId']);
- if (!$tpl)
- continue;
-
- $n = Util::localizedString($tpl, 'name');
-
- $what = '';
- switch ($tpl['typeCat'])
- {
- case -3:
- $what = 'herb';
- break;
- case -4:
- $what = 'vein';
- break;
- case 9:
- $what = 'book';
- break;
- case 25:
- $what = 'pool';
- break;
- case 0:
- if ($tpl['type'] == 19)
- $what = 'mail';
- break;
- case -6:
- if ($tpl['spellFocusId'] == 1)
- $what = 'anvil';
- else if ($tpl['spellFocusId'] == 3)
- $what = 'forge';
-
- break;
- }
-
- if ($what)
- {
- $blob = array(
- 'coords' => [[$spawn['posX'], $spawn['posY']]],
- 'level' => $spawn['floor'],
- 'name' => $n,
- 'type' => Type::OBJECT,
- 'id' => $tpl['id']
- );
-
- if ($what == 'mail')
- $blob['side'] = (($tpl['A'] < 0 ? 0 : 0x1) | ($tpl['H'] < 0 ? 0 : 0x2));
-
- $addToSOM($what, $blob);
- }
-
- if ($tpl['startsQuests'])
- {
- $started = new QuestList(array(['qse.method', 1, '&'], ['qse.type', Type::OBJECT], ['qse.typeId', $tpl['id']]));
- if ($started->error)
- continue;
-
- // store data for misc tabs
- foreach ($started->getListviewData() as $id => $data)
- {
- if (!empty($started->rewards[$id][Type::ITEM]))
- $rewardsLV = array_merge($rewardsLV, array_keys($started->rewards[$id][Type::ITEM]));
-
- if (!empty($started->choices[$id][Type::ITEM]))
- $rewardsLV = array_merge($rewardsLV, array_keys($started->choices[$id][Type::ITEM]));
-
- $questsLV[$id] = $data;
- }
-
- $this->extendGlobalData($started->getJSGlobals());
-
- if (($tpl['A'] != -1) && ($_ = $started->getSOMData(SIDE_ALLIANCE)))
- $addToSOM('alliancequests', array(
- 'coords' => [[$spawn['posX'], $spawn['posY']]],
- 'level' => $spawn['floor'],
- 'name' => $n,
- 'type' => Type::OBJECT,
- 'id' => $tpl['id'],
- 'side' => (($tpl['A'] < 0 ? 0 : 0x1) | ($tpl['H'] < 0 ? 0 : 0x2)),
- 'quests' => array_values($_)
- ));
-
- if (($tpl['H'] != -1) && ($_ = $started->getSOMData(SIDE_HORDE)))
- $addToSOM('hordequests', array(
- 'coords' => [[$spawn['posX'], $spawn['posY']]],
- 'level' => $spawn['floor'],
- 'name' => $n,
- 'type' => Type::OBJECT,
- 'id' => $tpl['id'],
- 'side' => (($tpl['A'] < 0 ? 0 : 0x1) | ($tpl['H'] < 0 ? 0 : 0x2)),
- 'quests' => array_values($_)
- ));
- }
- }
-
- $flightNodes = [];
- foreach ($cSpawns as $spawn)
- {
- $tpl = $creatureSpawns->getEntry($spawn['typeId']);
- if (!$tpl)
- continue;
-
- $n = Util::localizedString($tpl, 'name');
- $sn = Util::localizedString($tpl, 'subname');
-
- $what = '';
- if ($tpl['npcflag'] & NPC_FLAG_REPAIRER)
- $what = 'repair';
- else if ($tpl['npcflag'] & NPC_FLAG_AUCTIONEER)
- $what = 'auctioneer';
- else if ($tpl['npcflag'] & NPC_FLAG_BANKER)
- $what = 'banker';
- else if ($tpl['npcflag'] & NPC_FLAG_BATTLEMASTER)
- $what = 'battlemaster';
- else if ($tpl['npcflag'] & NPC_FLAG_INNKEEPER)
- $what = 'innkeeper';
- else if ($tpl['npcflag'] & NPC_FLAG_TRAINER)
- $what = 'trainer';
- else if ($tpl['npcflag'] & NPC_FLAG_VENDOR)
- $what = 'vendor';
- else if ($tpl['npcflag'] & NPC_FLAG_FLIGHT_MASTER)
- {
- $flightNodes[$tpl['id']] = [$spawn['posX'], $spawn['posY']];
- $what = 'flightmaster';
- }
- else if ($tpl['npcflag'] & NPC_FLAG_STABLE_MASTER)
- $what = 'stablemaster';
- else if ($tpl['npcflag'] & NPC_FLAG_GUILD_MASTER)
- $what = 'guildmaster';
- else if ($tpl['npcflag'] & (NPC_FLAG_SPIRIT_HEALER | NPC_FLAG_SPIRIT_GUIDE))
- $what = 'spirithealer';
- else if ($creatureSpawns->isBoss())
- $what = 'boss';
- else if ($tpl['rank'] == 2 || $tpl['rank'] == 4)
- $what = 'rare';
-
- if ($what)
- $addToSOM($what, array(
- 'coords' => [[$spawn['posX'], $spawn['posY']]],
- 'level' => $spawn['floor'],
- 'name' => $n,
- 'type' => Type::NPC,
- 'id' => $tpl['id'],
- 'reacthorde' => $tpl['H'] ?: 1, // no neutral (0) setting
- 'reactalliance' => $tpl['A'] ?: 1,
- 'description' => $sn
- ));
-
- if ($tpl['startsQuests'])
- {
- $started = new QuestList(array(['qse.method', 1, '&'], ['qse.type', Type::NPC], ['qse.typeId', $tpl['id']]));
- if ($started->error)
- continue;
-
- // store data for misc tabs
- foreach ($started->getListviewData() as $id => $data)
- {
- if (!empty($started->rewards[$id][Type::ITEM]))
- $rewardsLV = array_merge($rewardsLV, array_keys($started->rewards[$id][Type::ITEM]));
-
- if (!empty($started->choices[$id][Type::ITEM]))
- $rewardsLV = array_merge($rewardsLV, array_keys($started->choices[$id][Type::ITEM]));
-
- $questsLV[$id] = $data;
- }
-
- $this->extendGlobalData($started->getJSGlobals());
-
- if (($tpl['A'] != -1) && ($_ = $started->getSOMData(SIDE_ALLIANCE)))
- $addToSOM('alliancequests', array(
- 'coords' => [[$spawn['posX'], $spawn['posY']]],
- 'level' => $spawn['floor'],
- 'name' => $n,
- 'type' => Type::NPC,
- 'id' => $tpl['id'],
- 'reacthorde' => $tpl['H'],
- 'reactalliance' => $tpl['A'],
- 'side' => (($tpl['A'] < 0 ? 0 : SIDE_ALLIANCE) | ($tpl['H'] < 0 ? 0 : SIDE_HORDE)),
- 'quests' => array_values($_)
- ));
-
- if (($tpl['H'] != -1) && ($_ = $started->getSOMData(SIDE_HORDE)))
- $addToSOM('hordequests', array(
- 'coords' => [[$spawn['posX'], $spawn['posY']]],
- 'level' => $spawn['floor'],
- 'name' => $n,
- 'type' => Type::NPC,
- 'id' => $tpl['id'],
- 'reacthorde' => $tpl['H'],
- 'reactalliance' => $tpl['A'],
- 'side' => (($tpl['A'] < 0 ? 0 : SIDE_ALLIANCE) | ($tpl['H'] < 0 ? 0 : SIDE_HORDE)),
- 'quests' => array_values($_)
- ));
- }
- }
-
- foreach ($aSpawns as $spawn)
- {
- $tpl = $atSpawns->getEntry($spawn['typeId']);
- if (!$tpl)
- continue;
-
- $addToSOM('areatrigger', array(
- 'coords' => [[$spawn['posX'], $spawn['posY']]],
- 'level' => $spawn['floor'],
- 'name' => Util::localizedString($tpl, 'name', true, true),
- 'type' => Type::AREATRIGGER,
- 'id' => $spawn['typeId'],
- 'description' => 'Type: '.Lang::areatrigger('types', $tpl['type'])
- ));
- }
-
- // remove unwanted indizes
- foreach ($som as $what => &$dataz)
- {
- if (empty($som[$what]))
- continue;
-
- foreach ($dataz as &$data)
- $data = array_values($data);
-
- if (!in_array($what, ['vein', 'herb', 'rare', 'pool']))
- {
- $foo = [];
- foreach ($dataz as $d)
- foreach ($d as $_)
- $foo[] = $_;
-
- $dataz = $foo;
- }
- }
-
- unset($data);
-
- // append paths between nodes
- if ($flightNodes)
- {
- // neutral nodes come last as the line is colored by the node it's attached to
- usort($som['flightmaster'], function($a, $b) {
- $n1 = $a['reactalliance'] == $a['reacthorde'];
- $n2 = $b['reactalliance'] == $b['reacthorde'];
-
- if ($n1 && !$n2)
- return 1;
-
- if (!$n1 && $n2)
- return -1;
-
- return 0;
- });
-
- $paths = DB::Aowow()->select('SELECT n1.typeId AS "0", n2.typeId AS "1" FROM ?_taxipath p JOIN ?_taxinodes n1 ON n1.id = p.startNodeId JOIN ?_taxinodes n2 ON n2.id = p.endNodeId WHERE n1.typeId IN (?a) AND n2.typeId IN (?a)', array_keys($flightNodes), array_keys($flightNodes));
-
- foreach ($paths as $k => $path)
- {
- foreach ($som['flightmaster'] as &$fm)
- {
- if ($fm['id'] != $path[0] && $fm['id'] != $path[1])
- continue;
-
- if ($fm['id'] == $path[0])
- $fm['paths'][] = $flightNodes[$path[1]];
-
- if ($fm['id'] == $path[1])
- $fm['paths'][] = $flightNodes[$path[0]];
-
- unset($paths[$k]);
- break;
- }
- }
- }
-
- // preselect bosses for raids/dungeons
- if (in_array($this->subject->getField('type'), [2, 3, 4, 5, 7, 8]))
- $som['instance'] = true;
-
- $this->map = array(
- 'data' => ['parent' => 'mapper-generic', 'zone' => $this->typeId],
- 'som' => $som
- );
- }
- else
- $this->map = false;
-
- $this->infobox = $infobox ? '[ul][li]'.implode('[/li][li]', $infobox).'[/li][/ul]' : null;
- $this->expansion = Util::$expansionString[$this->subject->getField('expansion')];
- $this->redButtons = array(
- BUTTON_WOWHEAD => true,
- BUTTON_LINKS => ['type' => $this->type, 'typeId' => $this->typeId]
- );
-
- /*
- - associated with holiday?
- */
-
- /**************/
- /* Extra Tabs */
- /**************/
-
- // tab: NPCs
- if ($cSpawns && !$creatureSpawns->error)
- {
- $tabData = array(
- 'data' => array_values($creatureSpawns->getListviewData()),
- 'note' => sprintf(Util::$filterResultString, '?npcs&filter=cr=6;crs='.$this->typeId.';crv=0')
- );
-
- if ($creatureSpawns->getMatches() > CFG_SQL_LIMIT_DEFAULT)
- $tabData['_truncated'] = 1;
-
- $this->extendGlobalData($creatureSpawns->getJSGlobals(GLOBALINFO_SELF));
-
- $this->lvTabs[] = ['creature', $tabData];
- }
-
- // tab: Objects
- if ($oSpawns && !$objectSpawns->error)
- {
- $tabData = array(
- 'data' => array_values($objectSpawns->getListviewData()),
- 'note' => sprintf(Util::$filterResultString, '?objects&filter=cr=1;crs='.$this->typeId.';crv=0')
- );
-
- if ($objectSpawns->getMatches() > CFG_SQL_LIMIT_DEFAULT)
- $tabData['_truncated'] = 1;
-
- $this->extendGlobalData($objectSpawns->getJSGlobals(GLOBALINFO_SELF));
-
- $this->lvTabs[] = ['object', $tabData];
- }
-
- // tab: Quests [data collected by SOM-routine]
- if ($questsLV)
- {
- $tabData = ['quest', ['data' => array_values($questsLV)]];
-
- foreach (Game::$questClasses as $parent => $children)
- {
- if (in_array($this->typeId, $children))
- {
- $tabData[1]['note'] = '$$WH.sprintf(LANG.lvnote_zonequests, '.$parent.', '.$this->typeId.',"'.$this->subject->getField('name', true).'", '.$this->typeId.')';
- break;
- }
- }
-
- $this->lvTabs[] = $tabData;
- }
-
- // tab: item-quest starter
- // select every quest starter, that is a drop
- $questStartItem = DB::Aowow()->select('
- SELECT qse.typeId AS ARRAY_KEY, moreType, moreTypeId, moreZoneId
- FROM ?_quests_startend qse JOIN ?_source src ON src.type = qse.type AND src.typeId = qse.typeId
- WHERE src.src2 IS NOT NULL AND qse.type = ?d AND (moreZoneId = ?d OR (moreType = ?d AND moreTypeId IN (?a)) OR (moreType = ?d AND moreTypeId IN (?a)))',
- Type::ITEM, $this->typeId,
- Type::NPC, array_unique(array_column($cSpawns, 'typeId')) ?: [0],
- Type::OBJECT, array_unique(array_column($oSpawns, 'typeId')) ?: [0]
- );
-
- if ($questStartItem)
- {
- $qsiList = new ItemList(array(['id', array_keys($questStartItem)]));
- if (!$qsiList->error)
- {
- $this->lvTabs[] = ['item', array(
- 'data' => array_values($qsiList->getListviewData()),
- 'name' => '$LANG.tab_startsquest',
- 'id' => 'starts-quest'
- )];
-
- $this->extendGlobalData($qsiList->getJSGlobals(GLOBALINFO_SELF));
- }
- }
-
- // tab: Quest Rewards [ids collected by SOM-routine]
- if ($rewardsLV)
- {
- $rewards = new ItemList(array(['id', array_unique($rewardsLV)]));
- if (!$rewards->error)
- {
- $this->lvTabs[] = ['item', array(
- 'data' => array_values($rewards->getListviewData()),
- 'name' => '$LANG.tab_questrewards',
- 'id' => 'quest-rewards',
- 'note' => sprintf(Util::$filterResultString, '?items&filter=cr=126;crs='.$this->typeId.';crv=0')
- )];
-
- $this->extendGlobalData($rewards->getJSGlobals(GLOBALINFO_SELF));
- }
- }
-
- // tab: achievements
-
- // tab: fished in zone
- $fish = new Loot();
- if ($fish->getByContainer(LOOT_FISHING, $this->typeId))
- {
- $this->extendGlobalData($fish->jsGlobals);
- $xCols = array_merge(['$Listview.extraCols.percent'], $fish->extraCols);
-
- foreach ($fish->iterate() as $lv)
- {
- if (!$lv['quest'])
- continue;
-
- $xCols = array_merge($xCols, ['$Listview.extraCols.condition']);
-
- $reqQuest[$lv['id']] = 0;
-
- $lv['condition'][0][$this->typeId][] = [[CND_QUESTTAKEN, &$reqQuest[$lv['id']]]];
- }
-
- $this->lvTabs[] = ['item', array(
- 'data' => array_values($fish->getResult()),
- 'name' => '$LANG.tab_fishing',
- 'id' => 'fishing',
- 'extraCols' => array_unique($xCols),
- 'hiddenCols' => ['side']
- )];
- }
-
- // tab: spells
- if ($saData = DB::World()->select('SELECT * FROM spell_area WHERE area = ?d', $this->typeId))
- {
- $spells = new SpellList(array(['id', array_column($saData, 'spell')]));
- if (!$spells->error)
- {
- $lvSpells = $spells->getListviewData();
- $this->extendGlobalData($spells->getJSGlobals());
-
- $extra = false;
- foreach ($saData as $a)
- {
- if (empty($lvSpells[$a['spell']]))
- continue;
-
- $condition = [];
- if ($a['aura_spell'])
- {
- $this->extendGlobalIds(Type::SPELL, abs($a['aura_spell']));
- $condition[0][$this->typeId][] = [[$a['aura_spell'] > 0 ? CND_AURA : -CND_AURA, abs($a['aura_spell'])]];
- }
-
- if ($a['quest_start']) // status for quests needs work
- {
- $this->extendGlobalIds(Type::QUEST, $a['quest_start']);
- $group = [];
- for ($i = 0; $i < 7; $i++)
- {
- if (!($a['quest_start_status'] & (1 << $i)))
- continue;
-
- if ($i == 0)
- $group[] = [CND_QUEST_NONE, $a['quest_start']];
- else if ($i == 1)
- $group[] = [CND_QUEST_COMPLETE, $a['quest_start']];
- else if ($i == 3)
- $group[] = [CND_QUESTTAKEN, $a['quest_start']];
- else if ($i == 6)
- $group[] = [CND_QUESTREWARDED, $a['quest_start']];
- }
-
- if ($group)
- $condition[0][$this->typeId][] = $group;
- }
-
- if ($a['quest_end'] && $a['quest_end'] != $a['quest_start'])
- {
- $this->extendGlobalIds(Type::QUEST, $a['quest_end']);
- $group = [];
- for ($i = 0; $i < 7; $i++)
- {
- if (!($a['quest_end_status'] & (1 << $i)))
- continue;
-
- if ($i == 0)
- $group[] = [-CND_QUEST_NONE, $a['quest_end']];
- else if ($i == 1)
- $group[] = [-CND_QUEST_COMPLETE, $a['quest_end']];
- else if ($i == 3)
- $group[] = [-CND_QUESTTAKEN, $a['quest_end']];
- else if ($i == 6)
- $group[] = [-CND_QUESTREWARDED, $a['quest_end']];
- }
-
- if ($group)
- $condition[0][$this->typeId][] = $group;
- }
-
- if ($a['racemask'])
- {
- $foo = [];
- for ($i = 0; $i < 11; $i++)
- if ($a['racemask'] & (1 << $i))
- $foo[] = $i + 1;
-
- $this->extendGlobalIds(Type::CHR_RACE, ...$foo);
- $condition[0][$this->typeId][] = [[CND_RACE, $a['racemask']]];
- }
-
- if ($a['gender'] != 2) // 2: both
- $condition[0][$this->typeId][] = [[CND_GENDER, $a['gender'] + 1]];
-
- if ($condition)
- {
- $extra = true;
- $lvSpells[$a['spell']] = array_merge($lvSpells[$a['spell']], ['condition' => $condition]);
- }
- }
-
- $tabData = array(
- 'data' => array_values($lvSpells),
- 'hiddenCols' => ['skill']
- );
-
- if ($extra)
- $tabData['extraCols'] = ['$Listview.extraCols.condition'];
-
- $this->lvTabs[] = ['spell', $tabData];
- }
- }
-
- // tab: subzones
- $subZones = new ZoneList(array(['parentArea', $this->typeId]));
- if (!$subZones->error)
- {
- $this->lvTabs[] = ['zone', array(
- 'data' => array_values($subZones->getListviewData()),
- 'name' => '$LANG.tab_zones',
- 'id' => 'subzones',
- 'hiddenCols' => ['territory', 'instancetype']
- )];
-
- $this->extendGlobalData($subZones->getJSGlobals(GLOBALINFO_SELF));
- }
-
- // tab: sound (including subzones; excluding parents)
- $areaIds = [];
- if (!$subZones->error)
- $areaIds = $subZones->getFoundIDs();
-
- $areaIds[] = $this->typeId;
-
- $soundIds = [];
- $zoneMusic = DB::Aowow()->select('
- SELECT
- x.soundId AS ARRAY_KEY, x.soundId, x.worldStateId, x.worldStateValue, x.type
- FROM (
- SELECT ambienceDay AS soundId, worldStateId, worldStateValue, 1 AS `type` FROM ?_zones_sounds WHERE id IN (?a) AND ambienceDay > 0 UNION
- SELECT ambienceNight AS soundId, worldStateId, worldStateValue, 1 AS `type` FROM ?_zones_sounds WHERE id IN (?a) AND ambienceNight > 0 UNION
- SELECT musicDay AS soundId, worldStateId, worldStateValue, 2 AS `type` FROM ?_zones_sounds WHERE id IN (?a) AND musicDay > 0 UNION
- SELECT musicNight AS soundId, worldStateId, worldStateValue, 2 AS `type` FROM ?_zones_sounds WHERE id IN (?a) AND musicNight > 0 UNION
- SELECT intro AS soundId, worldStateId, worldStateValue, 3 AS `type` FROM ?_zones_sounds WHERE id IN (?a) AND intro > 0
- ) x
- GROUP BY
- x.soundId, x.worldStateId, x.worldStateValue
- ', $areaIds, $areaIds, $areaIds, $areaIds, $areaIds);
-
- if ($sSpawns = DB::Aowow()->selectCol('SELECT typeId FROM ?_spawns WHERE areaId = ?d AND type = ?d', $this->typeId, Type::SOUND))
- $soundIds = array_merge($soundIds, $sSpawns);
-
- if ($zoneMusic)
- $soundIds = array_merge($soundIds, array_column($zoneMusic, 'soundId'));
-
- if ($soundIds)
- {
- $music = new SoundList(array(['id', array_unique($soundIds)]));
- if (!$music->error)
- {
- // tab
- $data = $music->getListviewData();
- $tabData = [];
-
- if (array_filter(array_column($zoneMusic, 'worldStateId')))
- {
- $tabData['extraCols'] = ['$Listview.extraCols.condition'];
-
- foreach ($soundIds as $sId)
- if (!empty($zoneMusic[$sId]['worldStateId']))
- $data[$sId]['condition'][0][$this->typeId][] = [[CND_WORLD_STATE, $zoneMusic[$sId]['worldStateId'], $zoneMusic[$sId]['worldStateValue']]];
- }
-
- $tabData['data'] = array_values($data);
-
- $this->lvTabs[] = ['sound', $tabData];
-
- $this->extendGlobalData($music->getJSGlobals(GLOBALINFO_SELF));
-
- // audio controls
- // ambience
- if ($sounds = array_filter($zoneMusic, function ($x) { return $x['type'] == 1; } ))
- foreach ($sounds as $sId => $_)
- if (!empty($data[$sId]['files']))
- foreach ($data[$sId]['files'] as $f)
- $this->zoneMusic['ambience'][] = $f;
-
- // music
- if ($sounds = array_filter($zoneMusic, function ($x) { return $x['type'] == 2; } ))
- foreach ($sounds as $sId => $_)
- if (!empty($data[$sId]['files']))
- foreach ($data[$sId]['files'] as $f)
- $this->zoneMusic['music'][] = $f;
-
- // intro
- if ($sounds = array_filter($zoneMusic, function ($x) { return $x['type'] == 3; } ))
- foreach ($sounds as $sId => $_)
- if (!empty($data[$sId]['files']))
- foreach ($data[$sId]['files'] as $f)
- $this->zoneMusic['intro'][] = $f;
-
- }
- }
- }
-
- protected function generatePath()
- {
- $this->path[] = $this->subject->getField('category');
-
- if (in_array($this->subject->getField('category'), [2, 3]))
- $this->path[] = $this->subject->getField('expansion');
- }
-
- protected function generateTitle()
- {
- array_unshift($this->title, $this->name, Util::ucFirst(Lang::game('zone')));
- }
-
-}
-
-?>
diff --git a/pages/zones.php b/pages/zones.php
deleted file mode 100644
index 47286f99..00000000
--- a/pages/zones.php
+++ /dev/null
@@ -1,182 +0,0 @@
-getCategoryFromUrl($pageParam);;
-
- parent::__construct($pageCall, $pageParam);
-
- $this->name = Util::ucFirst(Lang::game('zones'));
- }
-
- protected function generateContent()
- {
- $conditions = [CFG_SQL_LIMIT_NONE];
- $visibleCols = [];
- $hiddenCols = [];
- $mapFile = 0;
- $spawnMap = -1;
-
- if (!User::isInGroup(U_GROUP_EMPLOYEE)) // sub-areas and unused zones
- $conditions[] = [['cuFlags', CUSTOM_EXCLUDE_FOR_LISTVIEW, '&'], 0];
-
- if ($this->category)
- {
- $conditions[] = ['z.category', $this->category[0]];
- $hiddenCols[] = 'category';
-
- if (isset($this->category[1]) && in_array($this->category[0], [2, 3]))
- $conditions[] = ['z.expansion', $this->category[1]];
-
- if (empty($this->category[1]))
- {
- switch ($this->category[0])
- {
- case 0: $mapFile = -3; $spawnMap = 0; break;
- case 1: $mapFile = -6; $spawnMap = 1; break;
- case 8: $mapFile = -2; $spawnMap = 530; break;
- case 10: $mapFile = -5; $spawnMap = 571; break;
- }
- }
-
- switch ($this->category[0])
- {
- case 6:
- case 2:
- case 3:
- array_push($visibleCols, 'level', 'players');
- case 9:
- $hiddenCols[] = 'territory';
- break;
- }
- }
-
- $zones = new ZoneList($conditions);
-
- if (!$zones->hasSetFields(['type']))
- $hiddenCols[] = 'instancetype';
-
- $tabData = ['data' => array_values($zones->getListviewData())];
-
- if ($visibleCols)
- $tabData['visibleCols'] = $visibleCols;
-
- if ($hiddenCols)
- $tabData['hiddenCols'] = $hiddenCols;
-
- $this->map = null;
- $this->lvTabs[] = ['zone', $tabData];
-
- // create flight map
- if ($mapFile)
- {
- $somData = ['flightmaster' => []];
- $nodes = DB::Aowow()->select('SELECT id AS ARRAY_KEY, tn.* FROM ?_taxinodes tn WHERE mapId = ?d AND type <> 0 AND typeId <> 0', $spawnMap);
- $paths = DB::Aowow()->select('
- SELECT IF(tn1.reactA = tn1.reactH AND tn2.reactA = tn2.reactH, 1, 0) AS neutral,
- tp.startNodeId AS startId,
- tn1.posX AS startPosX,
- tn1.posY AS startPosY,
- tp.endNodeId AS endId,
- tn2.posX AS endPosX,
- tn2.posY AS endPosY
- FROM ?_taxipath tp,
- ?_taxinodes tn1,
- ?_taxinodes tn2
- WHERE tn1.Id = tp.endNodeId AND
- tn2.Id = tp.startNodeId AND
- tn1.type <> 0 AND
- tn2.type <> 0 AND
- (tp.startNodeId IN (?a) OR tp.EndNodeId IN (?a))
- ', array_keys($nodes), array_keys($nodes));
-
- foreach ($nodes as $i => $n)
- {
- $neutral = $n['reactH'] == $n['reactA'];
-
- $data = array(
- 'coords' => [[$n['posX'], $n['posY']]],
- 'level' => 0, // floor
- 'name' => Util::localizedString($n, 'name'),
- 'type' => $n['type'],
- 'id' => $n['typeId'],
- 'reacthorde' => $n['reactH'],
- 'reactalliance' => $n['reactA'],
- 'paths' => []
- );
-
- foreach ($paths as $j => $p)
- {
- if ($i != $p['startId'] && $i != $p['endId'])
- continue;
-
- if ($i == $p['startId'] && (!$neutral || $p['neutral']))
- {
- $data['paths'][] = [$p['startPosX'], $p['startPosY']];
- unset($paths[$j]);
- }
- else if ($i == $p['endId'] && (!$neutral || $p['neutral']))
- {
- $data['paths'][] = [$p['endPosX'], $p['endPosY']];
- unset($paths[$j]);
- }
- }
-
- if (empty($data['paths']))
- unset($data['paths']);
-
- $somData['flightmaster'][] = $data;
- }
-
- $this->map = array(
- 'data' => array(
- 'zone' => $mapFile,
- 'zoom' => 1,
- 'overlay' => true,
- 'zoomable' => false,
- 'parent' => 'mapper-generic'
- ),
- 'som' => $somData,
- 'mapperData' => [$mapFile => new stdClass()]
- );
- }
- }
-
- protected function generateTitle()
- {
- if ($this->category)
- {
- if (isset($this->category[1]))
- array_unshift($this->title, Lang::game('expansions', $this->category[1]));
-
- array_unshift($this->title, Lang::zone('cat', $this->category[0]));
- }
- }
-
- protected function generatePath()
- {
- foreach ($this->category as $c)
- $this->path[] = $c;
- }
-}
-
-
-?>
diff --git a/prQueue b/prQueue
index 0ed859a2..20b12827 100755
--- a/prQueue
+++ b/prQueue
@@ -1,6 +1,10 @@
+#!/usr/bin/env php
'char',
+ Type::GUILD => 'guild',
+ Type::ARENA_TEAM => 'arena team'
+ };
- DB::Aowow()->query('UPDATE ?_profiler_sync SET status = ?d, errorCode = ?d WHERE realm = ?d AND realmGUID = ?d AND type = ?d', PR_QUEUE_STATUS_ERROR, PR_QUEUE_ERROR_CHAR, $realmId, $realmGUID, $type);
- trigger_error('prQueue - unknown '.$what.' guid #'.$realmGUID.' on realm #'.$realmId.' to sync into profiler.', E_USER_WARNING);
- CLI::write('unknown '.$what.' guid #'.$realmGUID.' on realm #'.$realmId.' to sync into profiler.', CLI::LOG_WARN);
+ $msg = match ($fetchResult)
+ {
+ Profiler::FETCH_RESULT_ERR_NAME_EMPTY => 'Subject has an empty name and was skipped.',
+ Profiler::FETCH_RESULT_ERR_NOT_FOUND => 'Subject was not found. Truncating local placeholder.',
+ Profiler::FETCH_RESULT_ERR_NO_MEMBERS => 'Subject has no members. Truncating local placeholder.',
+ Profiler::FETCH_RESULT_ERR_INTERNAL => 'Internal Error - Data stub is missing.'
+ };
+
+ trigger_error('prQueue - [realm: '.$realmId.' '.$what.' guid: '.$realmGUID.'] '.$msg, E_USER_WARNING);
+
+ DB::Aowow()->qry('UPDATE ::profiler_sync SET `status` = %i, `errorCode` = %i WHERE `realm` = %i AND `realmGUID` = %i AND `type` = %i', PR_QUEUE_STATUS_ERROR, PR_QUEUE_ERROR_CHAR, $realmId, $realmGUID, $type);
};
-// if (CFG_PROFILER_ENABLE) - wont work because it is not redefined if changed in config
-while (DB::Aowow()->selectCell('SELECT value FROM ?_config WHERE `key` = "profiler_enable"'))
+while (Cfg::get('PROFILER_ENABLE', true))
{
- if (($tDiff = (microtime(true) - $tCycle)) < (CFG_PROFILER_QUEUE_DELAY / 1000))
+ $delay = Cfg::get('PROFILER_QUEUE_DELAY') / 1000;
+ if (($tDiff = (microtime(true) - $tCycle)) < $delay)
{
- $wait = (CFG_PROFILER_QUEUE_DELAY / 1000) - $tDiff;
+ $wait = $delay - $tDiff;
CLI::write('sleeping '.Lang::nf($wait, 2).'s..');
usleep($wait * 1000 * 1000);
}
- $row = DB::Aowow()->selectRow('SELECT * FROM ?_profiler_sync WHERE status = ?d ORDER BY requestTime ASC', PR_QUEUE_STATUS_WAITING);
+ $row = DB::Aowow()->selectRow('SELECT * FROM ::profiler_sync WHERE `status` = %i ORDER BY `requestTime` ASC', PR_QUEUE_STATUS_WAITING);
if (!$row)
{
// nothing more to do
@@ -65,49 +76,64 @@ while (DB::Aowow()->selectCell('SELECT value FROM ?_config WHERE `key` = "profil
if (empty(Profiler::getRealms()[$row['realm']]))
{
- DB::Aowow()->query('UPDATE ?_profiler_sync SET status = ?d, errorCode = ?d WHERE realm = ?d AND type = ?d AND typeId = ?d', PR_QUEUE_STATUS_ERROR, PR_QUEUE_ERROR_ARMORY, $row['realm'], $row['type'], $row['typeId']);
- CLI::write('realm #'.$row['realm'].' for subject guid '.$row['realmGUID'].' is undefined', CLI::LOG_WARN);
+ DB::Aowow()->qry('UPDATE ::profiler_sync SET `status` = %i, `errorCode` = %i WHERE `realm` = %i AND `type` = %i AND `typeId` = %i', PR_QUEUE_STATUS_ERROR, PR_QUEUE_ERROR_ARMORY, $row['realm'], $row['type'], $row['typeId']);
+ CLI::write('realm #'.$row['realm'].' for subject guid '.$row['realmGUID'].' is missing/inaccessible.', CLI::LOG_WARN);
continue;
}
else
- DB::Aowow()->query('UPDATE ?_profiler_sync SET status = ?d WHERE realm = ?d AND type = ?d AND typeId = ?d', PR_QUEUE_STATUS_WORKING, $row['realm'], $row['type'], $row['typeId']);
+ DB::Aowow()->qry('UPDATE ::profiler_sync SET `status` = %i WHERE `realm` = %i AND `type` = %i AND `typeId` = %i', PR_QUEUE_STATUS_WORKING, $row['realm'], $row['type'], $row['typeId']);
switch ($row['type'])
{
- case TYPE_PROFILE:
- if (!Profiler::getCharFromRealm($row['realm'], $row['realmGUID']))
+ case Type::PROFILE:
+ switch ($result = Profiler::getCharFromRealm($row['realm'], $row['realmGUID']))
{
- $error(TYPE_PROFILE, $row['realmGUID'], $row['realm']);
- continue 2;
+ case Profiler::FETCH_RESULT_OK_UNCHANGED:
+ CLI::write('char #'.$row['realmGUID'].' on realm #'.$row['realm'].' did not log in since last update. skipping...');
+ case Profiler::FETCH_RESULT_OK:
+ break 2;
+ case Profiler::FETCH_RESULT_ERR_NAME_EMPTY:
+ case Profiler::FETCH_RESULT_ERR_NOT_FOUND:
+ DB::Aowow()->qry('DELETE FROM ::profiler_profiles WHERE `realm` = %i AND `realmGUID` = %i', $row['realm'], $row['realmGUID']);
+ default:
+ $error(Type::PROFILE, $row['realmGUID'], $row['realm'], $result);
+ continue 3;
}
-
- break;
- case TYPE_GUILD:
- if (!Profiler::getGuildFromRealm($row['realm'], $row['realmGUID']))
+ case Type::GUILD:
+ switch ($result = Profiler::getGuildFromRealm($row['realm'], $row['realmGUID']))
{
- $error(TYPE_GUILD, $row['realmGUID'], $row['realm']);
- continue 2;
+ case Profiler::FETCH_RESULT_OK:
+ break 2;
+ case Profiler::FETCH_RESULT_ERR_NAME_EMPTY:
+ case Profiler::FETCH_RESULT_ERR_NOT_FOUND:
+ case Profiler::FETCH_RESULT_ERR_NO_MEMBERS:
+ DB::Aowow()->qry('DELETE FROM ::profiler_guild WHERE `realm` = %i AND `realmGUID` = %i', $row['realm'], $row['realmGUID']);
+ default:
+ $error(Type::GUILD, $row['realmGUID'], $row['realm'], $result);
+ continue 3;
}
-
- break;
- case TYPE_ARENA_TEAM:
- if (!Profiler::getArenaTeamFromRealm($row['realm'], $row['realmGUID']))
+ case Type::ARENA_TEAM:
+ switch ($result = Profiler::getArenaTeamFromRealm($row['realm'], $row['realmGUID']))
{
- $error(TYPE_ARENA_TEAM, $row['realmGUID'], $row['realm']);
- continue 2;
+ case Profiler::FETCH_RESULT_OK:
+ break 2;
+ case Profiler::FETCH_RESULT_ERR_NAME_EMPTY:
+ case Profiler::FETCH_RESULT_ERR_NOT_FOUND:
+ case Profiler::FETCH_RESULT_ERR_NO_MEMBERS:
+ DB::Aowow()->qry('DELETE FROM ::profiler_arena_team WHERE `realm` = %i AND `realmGUID` = %i', $row['realm'], $row['realmGUID']);
+ default:
+ $error(Type::ARENA_TEAM, $row['realmGUID'], $row['realm'], $result);
+ continue 3;
}
-
- break;
default:
- DB::Aowow()->query('DELETE FROM ?_profiler_sync WHERE realm = ?d AND type = ?d AND typeId = ?d', $row['realm'], $row['type'], $row['typeId']);
+ DB::Aowow()->qry('DELETE FROM ::profiler_sync WHERE realm = %i AND type = %i AND typeId = %i', $row['realm'], $row['type'], $row['typeId']);
trigger_error('prQueue - unknown type #'.$row['type'].' to sync into profiler. Removing from queue...', E_USER_ERROR);
- CLI::write('unknown type #'.$row['type'].' to sync into profiler. Removing from queue...', CLI::LOG_ERROR);
}
$tCycle = microtime(true);
// mark as ready
- DB::Aowow()->query('UPDATE ?_profiler_sync SET status = ?d, errorCode = 0 WHERE realm = ?d AND type = ?d AND typeId = ?d', PR_QUEUE_STATUS_READY, $row['realm'], $row['type'], $row['typeId']);
+ DB::Aowow()->qry('UPDATE ::profiler_sync SET `status` = %i, `errorCode` = 0 WHERE `realm` = %i AND `type` = %i AND `typeId` = %i', PR_QUEUE_STATUS_READY, $row['realm'], $row['type'], $row['typeId']);
}
Profiler::queueFree();
diff --git a/setup/db_structure.sql b/setup/db_structure.sql
deleted file mode 100644
index 26b984fb..00000000
--- a/setup/db_structure.sql
+++ /dev/null
@@ -1,3259 +0,0 @@
--- MySQL dump 10.16 Distrib 10.2.10-MariaDB, for debian-linux-gnu (x86_64)
---
--- Host: localhost Database: sarjuuk_aowow
--- ------------------------------------------------------
--- Server version 10.2.10-MariaDB-10.2.10+maria~xenial-log
-
-/*!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 utf8 */;
-/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */;
-/*!40103 SET TIME_ZONE='+00:00' */;
-/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;
-/*!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' */;
-/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;
-
---
--- Table structure for table `aowow_account`
---
-
-DROP TABLE IF EXISTS `aowow_account`;
-/*!40101 SET @saved_cs_client = @@character_set_client */;
-/*!40101 SET character_set_client = utf8 */;
-CREATE TABLE `aowow_account` (
- `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
- `extId` int(10) unsigned NOT NULL COMMENT 'external user id',
- `user` varchar(64) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT 'login',
- `passHash` varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL,
- `displayName` varchar(64) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT 'nickname',
- `email` varchar(64) COLLATE utf8mb4_unicode_ci NOT NULL,
- `joinDate` int(10) unsigned NOT NULL COMMENT 'unixtime',
- `allowExpire` tinyint(1) unsigned NOT NULL,
- `dailyVotes` smallint(5) unsigned NOT NULL DEFAULT 0,
- `consecutiveVisits` smallint(5) unsigned NOT NULL DEFAULT 0,
- `curIP` varchar(45) COLLATE utf8mb4_unicode_ci NOT NULL,
- `prevIP` varchar(45) COLLATE utf8mb4_unicode_ci NOT NULL,
- `curLogin` int(15) unsigned NOT NULL COMMENT 'unixtime',
- `prevLogin` int(15) unsigned NOT NULL,
- `locale` tinyint(4) unsigned NOT NULL DEFAULT 0 COMMENT '0,2,3,6,8',
- `userGroups` smallint(5) unsigned NOT NULL DEFAULT 0 COMMENT 'bitmask',
- `avatar` varchar(50) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '' COMMENT 'icon-string for internal or id for upload',
- `title` varchar(50) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '' COMMENT 'user can obtain custom titles',
- `description` mediumtext COLLATE utf8mb4_unicode_ci NOT NULL COMMENT 'markdown formated',
- `excludeGroups` smallint(5) unsigned NOT NULL DEFAULT 1 COMMENT 'profiler - exclude bitmask',
- `userPerms` tinyint(4) unsigned NOT NULL DEFAULT 0 COMMENT 'bool isAdmin',
- `status` tinyint(4) unsigned NOT NULL DEFAULT 0 COMMENT 'flag, see defines',
- `statusTimer` int(10) unsigned NOT NULL DEFAULT 0,
- `token` varchar(40) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT 'creation & recovery',
- PRIMARY KEY (`id`),
- UNIQUE KEY `user` (`user`)
-) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=COMPACT;
-/*!40101 SET character_set_client = @saved_cs_client */;
-
---
--- Table structure for table `aowow_account_banned`
---
-
-DROP TABLE IF EXISTS `aowow_account_banned`;
-/*!40101 SET @saved_cs_client = @@character_set_client */;
-/*!40101 SET character_set_client = utf8 */;
-CREATE TABLE `aowow_account_banned` (
- `id` int(16) unsigned NOT NULL,
- `userId` int(10) unsigned NOT NULL COMMENT 'affected accountId',
- `staffId` int(10) unsigned NOT NULL COMMENT 'executive accountId',
- `typeMask` tinyint(4) unsigned NOT NULL COMMENT 'ACC_BAN_*',
- `start` int(10) unsigned NOT NULL COMMENT 'unixtime',
- `end` int(10) unsigned NOT NULL COMMENT 'automatic unban @ unixtime',
- `reason` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
- PRIMARY KEY (`id`),
- KEY `FK_acc_banned` (`userId`),
- CONSTRAINT `FK_acc_banned` FOREIGN KEY (`userId`) REFERENCES `aowow_account` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
-) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=COMPACT;
-/*!40101 SET character_set_client = @saved_cs_client */;
-
---
--- Table structure for table `aowow_account_bannedips`
---
-
-DROP TABLE IF EXISTS `aowow_account_bannedips`;
-/*!40101 SET @saved_cs_client = @@character_set_client */;
-/*!40101 SET character_set_client = utf8 */;
-CREATE TABLE `aowow_account_bannedips` (
- `ip` varchar(45) COLLATE utf8mb4_unicode_ci NOT NULL,
- `type` tinyint(4) NOT NULL COMMENT '0: onSignin; 1:onSignup',
- `count` smallint(6) NOT NULL COMMENT 'nFails',
- `unbanDate` int(11) NOT NULL COMMENT 'automatic remove @ unixtime',
- PRIMARY KEY (`ip`,`type`)
-) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-/*!40101 SET character_set_client = @saved_cs_client */;
-
---
--- Table structure for table `aowow_account_cookies`
---
-
-DROP TABLE IF EXISTS `aowow_account_cookies`;
-/*!40101 SET @saved_cs_client = @@character_set_client */;
-/*!40101 SET character_set_client = utf8 */;
-CREATE TABLE `aowow_account_cookies` (
- `userId` int(10) unsigned NOT NULL,
- `name` varchar(127) COLLATE utf8mb4_unicode_ci NOT NULL,
- `data` mediumtext COLLATE utf8mb4_unicode_ci NOT NULL,
- INDEX `userId` (`userId`) USING BTREE,
- UNIQUE KEY `userId_name` (`userId`,`name`) USING BTREE,
- CONSTRAINT `FK_acc_cookies` FOREIGN KEY (`userId`) REFERENCES `aowow_account` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
-) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=COMPACT;
-/*!40101 SET character_set_client = @saved_cs_client */;
-
---
--- Table structure for table `aowow_account_favorites`
---
-
-DROP TABLE IF EXISTS `aowow_account_favorites`;
-/*!40101 SET @saved_cs_client = @@character_set_client */;
-/*!40101 SET character_set_client = utf8 */;
-CREATE TABLE `aowow_account_favorites` (
- `userId` int(11) unsigned NOT NULL,
- `type` smallint(5) unsigned NOT NULL,
- `typeId` mediumint(8) unsigned NOT NULL,
- UNIQUE INDEX `userId_type_typeId` (`userId`, `type`, `typeId`),
- KEY `userId` (`userId`),
- CONSTRAINT `FK_acc_favorites` FOREIGN KEY (`userId`) REFERENCES `aowow_account` (`id`) ON UPDATE CASCADE ON DELETE CASCADE
-) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-/*!40101 SET character_set_client = @saved_cs_client */;
-
---
--- Table structure for table `aowow_account_excludes`
---
-
-DROP TABLE IF EXISTS `aowow_account_excludes`;
-/*!40101 SET @saved_cs_client = @@character_set_client */;
-/*!40101 SET character_set_client = utf8 */;
-CREATE TABLE `aowow_account_excludes` (
- `userId` int(11) unsigned NOT NULL,
- `type` smallint(5) unsigned NOT NULL,
- `typeId` mediumint(8) unsigned NOT NULL,
- `mode` tinyint(2) unsigned NOT NULL COMMENT '1: exclude; 2: include',
- UNIQUE KEY `userId_type_typeId` (`userId`,`type`,`typeId`),
- KEY `userId` (`userId`),
- CONSTRAINT `FK_acc_excludes` FOREIGN KEY (`userId`) REFERENCES `aowow_account` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
-) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-/*!40101 SET character_set_client = @saved_cs_client */;
-
---
--- Table structure for table `aowow_account_profiles`
---
-
-DROP TABLE IF EXISTS `aowow_account_profiles`;
-/*!40101 SET @saved_cs_client = @@character_set_client */;
-/*!40101 SET character_set_client = utf8 */;
-CREATE TABLE `aowow_account_profiles` (
- `accountId` int(10) unsigned NOT NULL,
- `profileId` int(10) unsigned NOT NULL,
- `extraFlags` int(10) unsigned NOT NULL,
- UNIQUE KEY `accountId_profileId` (`accountId`,`profileId`),
- KEY `accountId` (`accountId`),
- KEY `profileId` (`profileId`),
- CONSTRAINT `FK_account_id` FOREIGN KEY (`accountId`) REFERENCES `aowow_account` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
- CONSTRAINT `FK_profile_id` FOREIGN KEY (`profileId`) REFERENCES `aowow_profiler_profiles` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
-) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-/*!40101 SET character_set_client = @saved_cs_client */;
-
---
--- Table structure for table `aowow_account_reputation`
---
-
-DROP TABLE IF EXISTS `aowow_account_reputation`;
-/*!40101 SET @saved_cs_client = @@character_set_client */;
-/*!40101 SET character_set_client = utf8 */;
-CREATE TABLE `aowow_account_reputation` (
- `userId` int(10) unsigned NOT NULL,
- `action` tinyint(3) unsigned NOT NULL COMMENT 'e.g. upvote a comment',
- `amount` tinyint(3) unsigned NOT NULL,
- `sourceA` int(11) unsigned NOT NULL DEFAULT 0 COMMENT 'e.g. upvoting user',
- `sourceB` int(11) unsigned NOT NULL DEFAULT 0 COMMENT 'e.g. upvoted commentId',
- `date` int(10) unsigned NOT NULL DEFAULT 0,
- UNIQUE KEY `userId_action_source` (`userId`,`action`,`sourceA`,`sourceB`),
- KEY `userId` (`userId`),
- CONSTRAINT `FK_acc_rep` FOREIGN KEY (`userId`) REFERENCES `aowow_account` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
-) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=COMPACT COMMENT='reputation log';
-/*!40101 SET character_set_client = @saved_cs_client */;
-
---
--- Table structure for table `aowow_account_weightscale_data`
---
-
-DROP TABLE IF EXISTS `aowow_account_weightscale_data`;
-/*!40101 SET @saved_cs_client = @@character_set_client */;
-/*!40101 SET character_set_client = utf8 */;
-CREATE TABLE `aowow_account_weightscale_data` (
- `id` int(32) NOT NULL,
- `field` varchar(15) COLLATE utf8mb4_unicode_ci NOT NULL,
- `val` smallint(6) unsigned NOT NULL,
- KEY `id` (`id`),
- CONSTRAINT `FK_acc_weightscales` FOREIGN KEY (`id`) REFERENCES `aowow_account_weightscales` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
-) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-/*!40101 SET character_set_client = @saved_cs_client */;
-
---
--- Table structure for table `aowow_account_weightscales`
---
-
-DROP TABLE IF EXISTS `aowow_account_weightscales`;
-/*!40101 SET @saved_cs_client = @@character_set_client */;
-/*!40101 SET character_set_client = utf8 */;
-CREATE TABLE `aowow_account_weightscales` (
- `id` int(32) NOT NULL AUTO_INCREMENT,
- `userId` int(10) unsigned NOT NULL,
- `name` varchar(32) COLLATE utf8mb4_unicode_ci NOT NULL,
- `class` tinyint(3) unsigned NOT NULL DEFAULT 0,
- `icon` varchar(48) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '',
- PRIMARY KEY (`id`,`userId`),
- KEY `FK_acc_weights` (`userId`),
- CONSTRAINT `FK_acc_weights` FOREIGN KEY (`userId`) REFERENCES `aowow_account` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
-) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=COMPACT;
-/*!40101 SET character_set_client = @saved_cs_client */;
-
---
--- Table structure for table `aowow_achievement`
---
-
-DROP TABLE IF EXISTS `aowow_achievement`;
-/*!40101 SET @saved_cs_client = @@character_set_client */;
-/*!40101 SET character_set_client = utf8 */;
-CREATE TABLE `aowow_achievement` (
- `id` smallint(5) unsigned NOT NULL,
- `faction` tinyint(3) unsigned NOT NULL,
- `map` smallint(6) NOT NULL,
- `chainId` tinyint(3) unsigned NOT NULL,
- `chainPos` tinyint(3) unsigned NOT NULL,
- `category` smallint(6) unsigned NOT NULL,
- `parentCat` smallint(6) NOT NULL,
- `points` tinyint(3) unsigned NOT NULL,
- `orderInGroup` tinyint(3) unsigned NOT NULL,
- `iconId` smallint(5) unsigned NOT NULL DEFAULT 0,
- `iconIdBak` smallint(5) unsigned NOT NULL DEFAULT 0,
- `flags` smallint(5) unsigned NOT NULL,
- `reqCriteriaCount` tinyint(3) unsigned NOT NULL,
- `refAchievement` smallint(5) unsigned NOT NULL,
- `itemExtra` mediumint(8) unsigned NOT NULL,
- `cuFlags` int(10) unsigned NOT NULL COMMENT 'see defines.php for flags',
- `name_loc0` varchar(78) COLLATE utf8mb4_unicode_ci NOT NULL,
- `name_loc2` varchar(79) COLLATE utf8mb4_unicode_ci NOT NULL,
- `name_loc3` varchar(86) COLLATE utf8mb4_unicode_ci NOT NULL,
- `name_loc4` varchar(86) COLLATE utf8mb4_unicode_ci NOT NULL,
- `name_loc6` varchar(78) COLLATE utf8mb4_unicode_ci NOT NULL,
- `name_loc8` varchar(76) COLLATE utf8mb4_unicode_ci NOT NULL,
- `description_loc0` mediumtext COLLATE utf8mb4_unicode_ci NOT NULL,
- `description_loc2` mediumtext COLLATE utf8mb4_unicode_ci NOT NULL,
- `description_loc3` mediumtext COLLATE utf8mb4_unicode_ci NOT NULL,
- `description_loc4` mediumtext COLLATE utf8mb4_unicode_ci NOT NULL,
- `description_loc6` mediumtext COLLATE utf8mb4_unicode_ci NOT NULL,
- `description_loc8` mediumtext COLLATE utf8mb4_unicode_ci NOT NULL,
- `reward_loc0` varchar(74) COLLATE utf8mb4_unicode_ci NOT NULL,
- `reward_loc2` varchar(88) COLLATE utf8mb4_unicode_ci NOT NULL,
- `reward_loc3` varchar(92) COLLATE utf8mb4_unicode_ci NOT NULL,
- `reward_loc4` varchar(92) COLLATE utf8mb4_unicode_ci NOT NULL,
- `reward_loc6` varchar(83) COLLATE utf8mb4_unicode_ci NOT NULL,
- `reward_loc8` varchar(95) COLLATE utf8mb4_unicode_ci NOT NULL,
- PRIMARY KEY (`id`),
- KEY `iconId` (`iconId`)
-) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-/*!40101 SET character_set_client = @saved_cs_client */;
-
---
--- Table structure for table `aowow_achievementcategory`
---
-
-DROP TABLE IF EXISTS `aowow_achievementcategory`;
-/*!40101 SET @saved_cs_client = @@character_set_client */;
-/*!40101 SET character_set_client = utf8 */;
-CREATE TABLE `aowow_achievementcategory` (
- `id` smallint(5) unsigned NOT NULL DEFAULT '0',
- `parentCat` smallint(5) unsigned NOT NULL DEFAULT '0',
- `parentCat2` smallint(5) unsigned NOT NULL DEFAULT '0',
- PRIMARY KEY (`id`) USING BTREE
-) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-/*!40101 SET character_set_client = @saved_cs_client */;
-
---
--- Table structure for table `aowow_achievementcriteria`
---
-
-DROP TABLE IF EXISTS `aowow_achievementcriteria`;
-/*!40101 SET @saved_cs_client = @@character_set_client */;
-/*!40101 SET character_set_client = utf8 */;
-CREATE TABLE `aowow_achievementcriteria` (
- `id` smallint(6) unsigned NOT NULL,
- `refAchievementId` smallint(6) unsigned NOT NULL,
- `type` tinyint(3) unsigned NOT NULL,
- `value1` int(10) unsigned NOT NULL,
- `value2` int(10) unsigned NOT NULL,
- `value3` int(10) unsigned NOT NULL,
- `value4` int(10) unsigned NOT NULL,
- `value5` int(10) unsigned NOT NULL,
- `value6` int(10) unsigned NOT NULL,
- `name_loc0` varchar(92) COLLATE utf8mb4_unicode_ci NOT NULL,
- `name_loc2` varchar(104) COLLATE utf8mb4_unicode_ci NOT NULL,
- `name_loc3` varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL,
- `name_loc4` varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL,
- `name_loc6` varchar(119) COLLATE utf8mb4_unicode_ci NOT NULL,
- `name_loc8` varchar(118) COLLATE utf8mb4_unicode_ci NOT NULL,
- `completionFlags` tinyint(3) unsigned NOT NULL,
- `groupFlags` tinyint(3) unsigned NOT NULL,
- `timeLimit` smallint(5) unsigned NOT NULL,
- `order` smallint(5) unsigned NOT NULL,
- PRIMARY KEY (`id`),
- KEY `idx_achievement` (`refAchievementId`)
-) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-/*!40101 SET character_set_client = @saved_cs_client */;
-
---
--- Table structure for table `aowow_announcements`
---
-
-DROP TABLE IF EXISTS `aowow_announcements`;
-/*!40101 SET @saved_cs_client = @@character_set_client */;
-/*!40101 SET character_set_client = utf8 */;
-CREATE TABLE `aowow_announcements` (
- `id` int(16) NOT NULL AUTO_INCREMENT COMMENT 'iirc negative Ids cant be deleted',
- `page` varchar(256) COLLATE utf8mb4_unicode_ci NOT NULL,
- `name` varchar(256) COLLATE utf8mb4_unicode_ci NOT NULL,
- `groupMask` smallint(5) unsigned NOT NULL,
- `style` varchar(256) COLLATE utf8mb4_unicode_ci NOT NULL,
- `mode` tinyint(4) unsigned NOT NULL COMMENT '0:pageTop; 1:contentTop',
- `status` tinyint(4) unsigned NOT NULL COMMENT '0:disabled; 1:enabled; 2:deleted',
- `text_loc0` mediumtext COLLATE utf8mb4_unicode_ci NOT NULL,
- `text_loc2` mediumtext COLLATE utf8mb4_unicode_ci NOT NULL,
- `text_loc3` mediumtext COLLATE utf8mb4_unicode_ci NOT NULL,
- `text_loc4` mediumtext COLLATE utf8mb4_unicode_ci NOT NULL,
- `text_loc6` mediumtext COLLATE utf8mb4_unicode_ci NOT NULL,
- `text_loc8` mediumtext COLLATE utf8mb4_unicode_ci NOT NULL,
- PRIMARY KEY (`id`)
-) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-/*!40101 SET character_set_client = @saved_cs_client */;
-
---
--- Table structure for table `aowow_areatrigger`
---
-
-DROP TABLE IF EXISTS `aowow_areatrigger`;
-/*!40101 SET @saved_cs_client = @@character_set_client */;
-/*!40101 SET character_set_client = utf8 */;
-CREATE TABLE `aowow_areatrigger` (
- `id` int(10) unsigned NOT NULL,
- `cuFlags` int(10) unsigned NOT NULL,
- `type` smallint(5) unsigned NOT NULL,
- `name` varchar(100) NULL DEFAULT NULL,
- `orientation` float NOT NULL,
- `quest` mediumint(8) unsigned NULL DEFAULT NULL,
- `teleportA` smallint(5) unsigned NULL DEFAULT NULL,
- `teleportX` float unsigned NULL DEFAULT NULL,
- `teleportY` float unsigned NULL DEFAULT NULL,
- `teleportO` float NULL DEFAULT NULL,
- `teleportF` tinyint(4) unsigned NULL DEFAULT NULL,
- PRIMARY KEY (`id`),
- INDEX `quest` (`quest`),
- INDEX `type` (`type`)
-) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4 COLLATE='utf8mb4_general_ci' ;
-/*!40101 SET character_set_client = @saved_cs_client */;
-
---
--- Table structure for table `aowow_articles`
---
-
-DROP TABLE IF EXISTS `aowow_articles`;
-/*!40101 SET @saved_cs_client = @@character_set_client */;
-/*!40101 SET character_set_client = utf8 */;
-CREATE TABLE `aowow_articles` (
- `type` smallint DEFAULT NULL,
- `typeId` mediumint DEFAULT NULL,
- `locale` tinyint unsigned NOT NULL,
- `url` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL,
- `rev` tinyint unsigned NOT NULL DEFAULT '0',
- `editAccess` smallint unsigned NOT NULL DEFAULT '2',
- `article` mediumtext CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci COMMENT 'Markdown formated',
- `quickInfo` mediumtext CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci COMMENT 'Markdown formated',
- UNIQUE KEY `type` (`type`,`typeId`,`locale`,`rev`),
- UNIQUE KEY `url` (`url`,`locale`,`rev`)
-) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-/*!40101 SET character_set_client = @saved_cs_client */;
-
---
--- Table structure for table `aowow_classes`
---
-
-DROP TABLE IF EXISTS `aowow_classes`;
-/*!40101 SET @saved_cs_client = @@character_set_client */;
-/*!40101 SET character_set_client = utf8 */;
-CREATE TABLE `aowow_classes` (
- `id` int(16) NOT NULL,
- `fileString` varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL,
- `name_loc0` varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL,
- `name_loc2` varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL,
- `name_loc3` varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL,
- `name_loc4` varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL,
- `name_loc6` varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL,
- `name_loc8` varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL,
- `powerType` tinyint(4) NOT NULL,
- `raceMask` int(16) NOT NULL,
- `roles` int(16) NOT NULL,
- `skills` varchar(32) COLLATE utf8mb4_unicode_ci NOT NULL,
- `flags` mediumint(16) NOT NULL,
- `cuFlags` int(10) unsigned NOT NULL,
- `weaponTypeMask` int(32) NOT NULL,
- `armorTypeMask` int(32) NOT NULL,
- `expansion` tinyint(2) NOT NULL,
- PRIMARY KEY (`id`)
-) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-/*!40101 SET character_set_client = @saved_cs_client */;
-
---
--- Table structure for table `aowow_comments`
---
-
-DROP TABLE IF EXISTS `aowow_comments`;
-/*!40101 SET @saved_cs_client = @@character_set_client */;
-/*!40101 SET character_set_client = utf8 */;
-CREATE TABLE `aowow_comments` (
- `id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT 'Comment ID',
- `type` smallint(5) unsigned NOT NULL COMMENT 'Type of Page',
- `typeId` mediumint(9) NOT NULL COMMENT 'ID Of Page',
- `userId` int(10) unsigned DEFAULT NULL COMMENT 'User ID',
- `roles` smallint(5) unsigned NOT NULL,
- `body` text COLLATE utf8mb4_unicode_ci NOT NULL COMMENT 'Comment text',
- `date` int(11) NOT NULL COMMENT 'Comment timestap',
- `flags` smallint(6) NOT NULL DEFAULT 0 COMMENT 'deleted, outofdate, sticky',
- `replyTo` int(10) unsigned NOT NULL DEFAULT 0 COMMENT 'Reply To, comment ID',
- `editUserId` int(10) unsigned NOT NULL DEFAULT 0 COMMENT 'Last Edit User ID',
- `editDate` int(10) unsigned NOT NULL DEFAULT 0 COMMENT 'Last Edit Time',
- `editCount` tinyint(3) unsigned NOT NULL DEFAULT 0 COMMENT 'Count Of Edits',
- `deleteUserId` int(10) unsigned NOT NULL DEFAULT 0,
- `deleteDate` int(10) unsigned NOT NULL DEFAULT 0,
- `responseUserId` int(10) unsigned NOT NULL DEFAULT 0,
- `responseBody` text COLLATE utf8mb4_unicode_ci DEFAULT NULL,
- `responseRoles` smallint(5) unsigned NOT NULL DEFAULT 0,
- PRIMARY KEY (`id`),
- KEY `type_typeId` (`type`,`typeId`),
- KEY `FK_acc_co` (`userId`),
- CONSTRAINT `FK_acc_co` FOREIGN KEY (`userId`) REFERENCES `aowow_account` (`id`) ON DELETE SET NULL ON UPDATE CASCADE
-) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-/*!40101 SET character_set_client = @saved_cs_client */;
-
---
--- Table structure for table `aowow_config`
---
-
-DROP TABLE IF EXISTS `aowow_config`;
-/*!40101 SET @saved_cs_client = @@character_set_client */;
-/*!40101 SET character_set_client = utf8 */;
-CREATE TABLE `aowow_config` (
- `key` varchar(25) COLLATE utf8mb4_unicode_ci NOT NULL,
- `value` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
- `cat` tinyint(3) unsigned NOT NULL DEFAULT 5,
- `flags` tinyint(3) unsigned NOT NULL DEFAULT 0,
- `comment` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
- PRIMARY KEY (`key`)
-) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-/*!40101 SET character_set_client = @saved_cs_client */;
-
---
--- Table structure for table `aowow_creature`
---
-
-DROP TABLE IF EXISTS `aowow_creature`;
-/*!40101 SET @saved_cs_client = @@character_set_client */;
-/*!40101 SET character_set_client = utf8 */;
-CREATE TABLE `aowow_creature` (
- `id` mediumint(8) unsigned NOT NULL DEFAULT 0,
- `cuFlags` int(10) unsigned NOT NULL DEFAULT 0,
- `difficultyEntry1` mediumint(8) unsigned NOT NULL DEFAULT 0,
- `difficultyEntry2` mediumint(8) unsigned NOT NULL DEFAULT 0,
- `difficultyEntry3` mediumint(8) unsigned NOT NULL DEFAULT 0,
- `KillCredit1` int(10) unsigned NOT NULL DEFAULT 0,
- `KillCredit2` int(10) unsigned NOT NULL DEFAULT 0,
- `displayId1` mediumint(8) unsigned NOT NULL DEFAULT 0,
- `displayId2` mediumint(8) unsigned NOT NULL DEFAULT 0,
- `displayId3` mediumint(8) unsigned NOT NULL DEFAULT 0,
- `displayId4` mediumint(8) unsigned NOT NULL DEFAULT 0,
- `textureString` varchar(50) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
- `modelId` mediumint(8) NOT NULL,
- `humanoid` tinyint(1) unsigned NOT NULL DEFAULT 0,
- `iconString` varchar(50) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT 'first texture of first model for search (up to 11 other skins omitted..)',
- `name_loc0` varchar(100) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '0',
- `name_loc2` varchar(100) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
- `name_loc3` varchar(100) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
- `name_loc4` varchar(100) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
- `name_loc6` varchar(100) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
- `name_loc8` varchar(100) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
- `subname_loc0` varchar(100) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
- `subname_loc2` varchar(100) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
- `subname_loc3` varchar(100) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
- `subname_loc4` varchar(100) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
- `subname_loc6` varchar(100) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
- `subname_loc8` varchar(100) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
- `minLevel` tinyint(3) unsigned NOT NULL DEFAULT 1,
- `maxLevel` tinyint(3) unsigned NOT NULL DEFAULT 1,
- `exp` smallint(6) NOT NULL DEFAULT 0,
- `faction` smallint(5) unsigned NOT NULL DEFAULT 0,
- `npcflag` int(10) unsigned NOT NULL DEFAULT 0,
- `rank` tinyint(3) unsigned NOT NULL DEFAULT 0,
- `dmgSchool` tinyint(4) NOT NULL DEFAULT 0,
- `dmgMultiplier` float NOT NULL DEFAULT 1,
- `atkSpeed` int(10) unsigned NOT NULL DEFAULT 0,
- `rngAtkSpeed` int(10) unsigned NOT NULL DEFAULT 0,
- `mleVariance` float NOT NULL DEFAULT 1,
- `rngVariance` float NOT NULL DEFAULT 1,
- `unitClass` tinyint(3) unsigned NOT NULL DEFAULT 0,
- `unitFlags` int(10) unsigned NOT NULL DEFAULT 0,
- `unitFlags2` int(10) unsigned NOT NULL DEFAULT 0,
- `dynamicFlags` int(10) unsigned NOT NULL DEFAULT 0,
- `family` tinyint(4) NOT NULL DEFAULT 0,
- `trainerType` tinyint(4) NOT NULL DEFAULT 0,
- `trainerRequirement` smallint(5) unsigned NOT NULL DEFAULT 0,
- `dmgMin` float unsigned NOT NULL DEFAULT 0,
- `dmgMax` float unsigned NOT NULL DEFAULT 0,
- `mleAtkPwrMin` smallint(5) unsigned NOT NULL DEFAULT 0,
- `mleAtkPwrMax` smallint(5) unsigned NOT NULL DEFAULT 0,
- `rngAtkPwrMin` smallint(5) unsigned NOT NULL DEFAULT 0,
- `rngAtkPwrMax` smallint(5) unsigned NOT NULL DEFAULT 0,
- `type` tinyint(3) unsigned NOT NULL DEFAULT 0,
- `typeFlags` int(10) unsigned NOT NULL DEFAULT 0,
- `lootId` mediumint(8) unsigned NOT NULL DEFAULT 0,
- `pickpocketLootId` mediumint(8) unsigned NOT NULL DEFAULT 0,
- `skinLootId` mediumint(8) unsigned NOT NULL DEFAULT 0,
- `spell1` mediumint(8) unsigned NOT NULL DEFAULT 0,
- `spell2` mediumint(8) unsigned NOT NULL DEFAULT 0,
- `spell3` mediumint(8) unsigned NOT NULL DEFAULT 0,
- `spell4` mediumint(8) unsigned NOT NULL DEFAULT 0,
- `spell5` mediumint(8) unsigned NOT NULL DEFAULT 0,
- `spell6` mediumint(8) unsigned NOT NULL DEFAULT 0,
- `spell7` mediumint(8) unsigned NOT NULL DEFAULT 0,
- `spell8` mediumint(8) unsigned NOT NULL DEFAULT 0,
- `petSpellDataId` mediumint(8) unsigned NOT NULL DEFAULT 0,
- `vehicleId` mediumint(8) unsigned NOT NULL DEFAULT 0,
- `minGold` mediumint(8) unsigned NOT NULL DEFAULT 0,
- `maxGold` mediumint(8) unsigned NOT NULL DEFAULT 0,
- `aiName` varchar(50) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '',
- `healthMin` int(10) unsigned NOT NULL DEFAULT 1,
- `healthMax` int(10) unsigned NOT NULL DEFAULT 1,
- `manaMin` int(10) unsigned NOT NULL DEFAULT 1,
- `manaMax` int(10) unsigned NOT NULL DEFAULT 1,
- `armorMin` mediumint(8) unsigned NOT NULL DEFAULT 1,
- `armorMax` mediumint(8) unsigned NOT NULL DEFAULT 1,
- `resistance1` smallint(5) NOT NULL DEFAULT 0,
- `resistance2` smallint(5) NOT NULL DEFAULT 0,
- `resistance3` smallint(5) NOT NULL DEFAULT 0,
- `resistance4` smallint(5) NOT NULL DEFAULT 0,
- `resistance5` smallint(5) NOT NULL DEFAULT 0,
- `resistance6` smallint(5) NOT NULL DEFAULT 0,
- `racialLeader` tinyint(3) unsigned NOT NULL DEFAULT 0,
- `mechanicImmuneMask` int(10) unsigned NOT NULL DEFAULT 0,
- `flagsExtra` int(10) unsigned NOT NULL DEFAULT 0,
- `scriptName` varchar(50) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '',
- PRIMARY KEY (`id`),
- KEY `idx_name` (`name_loc0`),
- KEY `difficultyEntry1` (`difficultyEntry1`),
- KEY `difficultyEntry2` (`difficultyEntry2`),
- KEY `difficultyEntry3` (`difficultyEntry3`)
-) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-/*!40101 SET character_set_client = @saved_cs_client */;
-
---
--- Table structure for table `aowow_creature_sounds`
---
-
-DROP TABLE IF EXISTS `aowow_creature_sounds`;
-/*!40101 SET @saved_cs_client = @@character_set_client */;
-/*!40101 SET character_set_client = utf8 */;
-CREATE TABLE `aowow_creature_sounds` (
- `id` smallint(5) unsigned NOT NULL COMMENT 'CreatureDisplayInfo.dbc/id',
- `greeting` smallint(5) unsigned NOT NULL,
- `farewell` smallint(5) unsigned NOT NULL,
- `angry` smallint(5) unsigned NOT NULL,
- `exertion` smallint(5) unsigned NOT NULL,
- `exertioncritical` smallint(5) unsigned NOT NULL,
- `injury` smallint(5) unsigned NOT NULL,
- `injurycritical` smallint(5) unsigned NOT NULL,
- `death` smallint(5) unsigned NOT NULL,
- `stun` smallint(5) unsigned NOT NULL,
- `stand` smallint(5) unsigned NOT NULL,
- `footstep` smallint(5) unsigned NOT NULL,
- `aggro` smallint(5) unsigned NOT NULL,
- `wingflap` smallint(5) unsigned NOT NULL,
- `wingglide` smallint(5) unsigned NOT NULL,
- `alert` smallint(5) unsigned NOT NULL,
- `fidget` smallint(5) unsigned NOT NULL,
- `customattack` smallint(5) unsigned NOT NULL,
- `loop` smallint(5) unsigned NOT NULL,
- `jumpstart` smallint(5) unsigned NOT NULL,
- `jumpend` smallint(5) unsigned NOT NULL,
- `petattack` smallint(5) unsigned NOT NULL,
- `petorder` smallint(5) unsigned NOT NULL,
- `petdismiss` smallint(5) unsigned NOT NULL,
- `birth` smallint(5) unsigned NOT NULL,
- `spellcast` smallint(5) unsigned NOT NULL,
- `submerge` smallint(5) unsigned NOT NULL,
- `submerged` smallint(5) unsigned NOT NULL,
- `transform` smallint(5) unsigned NOT NULL,
- `transformanimated` smallint(5) unsigned NOT NULL,
- PRIMARY KEY (`id`)
-) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='!ATTENTION!\r\nthe primary key of this table is NOT a creatureId, but displayId\r\n\r\ncolumn names from LANG.sound_activities';
-/*!40101 SET character_set_client = @saved_cs_client */;
-
---
--- Table structure for table `aowow_creature_waypoints`
---
-
-DROP TABLE IF EXISTS `aowow_creature_waypoints`;
-/*!40101 SET @saved_cs_client = @@character_set_client */;
-/*!40101 SET character_set_client = utf8 */;
-CREATE TABLE `aowow_creature_waypoints` (
- `creatureOrPath` int(11) NOT NULL,
- `point` smallint(5) unsigned NOT NULL,
- `areaId` smallint(5) unsigned NOT NULL,
- `floor` tinyint(3) unsigned NOT NULL,
- `posX` float unsigned NOT NULL,
- `posY` float unsigned NOT NULL,
- `wait` mediumint(8) unsigned NOT NULL,
- PRIMARY KEY (`creatureOrPath`,`point`,`areaId`,`floor`)
-) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-/*!40101 SET character_set_client = @saved_cs_client */;
-
---
--- Table structure for table `aowow_currencies`
---
-
-DROP TABLE IF EXISTS `aowow_currencies`;
-/*!40101 SET @saved_cs_client = @@character_set_client */;
-/*!40101 SET character_set_client = utf8 */;
-CREATE TABLE `aowow_currencies` (
- `id` int(16) NOT NULL,
- `category` mediumint(8) NOT NULL,
- `cuFlags` int(10) unsigned NOT NULL,
- `iconId` smallint(5) unsigned NOT NULL DEFAULT 0,
- `itemId` int(16) NOT NULL,
- `cap` mediumint(8) unsigned NOT NULL,
- `name_loc0` varchar(64) COLLATE utf8mb4_unicode_ci NOT NULL,
- `name_loc2` varchar(64) COLLATE utf8mb4_unicode_ci NOT NULL,
- `name_loc3` varchar(64) COLLATE utf8mb4_unicode_ci NOT NULL,
- `name_loc4` varchar(64) COLLATE utf8mb4_unicode_ci NOT NULL,
- `name_loc6` varchar(64) COLLATE utf8mb4_unicode_ci NOT NULL,
- `name_loc8` varchar(64) COLLATE utf8mb4_unicode_ci NOT NULL,
- `description_loc0` varchar(256) COLLATE utf8mb4_unicode_ci NOT NULL,
- `description_loc2` varchar(256) COLLATE utf8mb4_unicode_ci NOT NULL,
- `description_loc3` varchar(256) COLLATE utf8mb4_unicode_ci NOT NULL,
- `description_loc4` varchar(256) COLLATE utf8mb4_unicode_ci NOT NULL,
- `description_loc6` varchar(256) COLLATE utf8mb4_unicode_ci NOT NULL,
- `description_loc8` varchar(256) COLLATE utf8mb4_unicode_ci NOT NULL,
- PRIMARY KEY (`id`),
- KEY `iconId` (`iconId`)
-) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-/*!40101 SET character_set_client = @saved_cs_client */;
-
---
--- Table structure for table `aowow_dbversion`
---
-
-DROP TABLE IF EXISTS `aowow_dbversion`;
-/*!40101 SET @saved_cs_client = @@character_set_client */;
-/*!40101 SET character_set_client = utf8 */;
-CREATE TABLE `aowow_dbversion` (
- `date` int(10) unsigned NOT NULL DEFAULT 0,
- `part` tinyint(3) unsigned NOT NULL DEFAULT 0,
- `sql` mediumtext COLLATE utf8mb4_unicode_ci DEFAULT NULL,
- `build` mediumtext COLLATE utf8mb4_unicode_ci DEFAULT NULL
-) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-/*!40101 SET character_set_client = @saved_cs_client */;
-
---
--- Table structure for table `aowow_emotes`
---
-
-DROP TABLE IF EXISTS `aowow_emotes`;
-/*!40101 SET @saved_cs_client = @@character_set_client */;
-/*!40101 SET character_set_client = utf8 */;
-CREATE TABLE `aowow_emotes` (
- `id` smallint(5) unsigned NOT NULL,
- `cmd` varchar(15) COLLATE utf8mb4_unicode_ci NOT NULL,
- `isAnimated` tinyint(1) unsigned NOT NULL,
- `cuFlags` int(10) unsigned NOT NULL,
- `target_loc0` varchar(65) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
- `target_loc2` varchar(70) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
- `target_loc3` varchar(95) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
- `target_loc4` varchar(95) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
- `target_loc6` varchar(90) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
- `target_loc8` varchar(70) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
- `noTarget_loc0` varchar(65) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
- `noTarget_loc2` varchar(110) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
- `noTarget_loc3` varchar(85) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
- `noTarget_loc4` varchar(85) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
- `noTarget_loc6` varchar(75) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
- `noTarget_loc8` varchar(60) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
- `self_loc0` varchar(65) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
- `self_loc2` varchar(115) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
- `self_loc3` varchar(85) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
- `self_loc4` varchar(85) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
- `self_loc6` varchar(75) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
- `self_loc8` varchar(70) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
- PRIMARY KEY (`id`)
-) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-/*!40101 SET character_set_client = @saved_cs_client */;
-
---
--- Table structure for table `aowow_emotes_aliasses`
---
-
-DROP TABLE IF EXISTS `aowow_emotes_aliasses`;
-/*!40101 SET @saved_cs_client = @@character_set_client */;
-/*!40101 SET character_set_client = utf8 */;
-CREATE TABLE `aowow_emotes_aliasses` (
- `id` smallint(6) unsigned NOT NULL,
- `locales` smallint(6) unsigned NOT NULL,
- `command` varchar(15) COLLATE utf8mb4_unicode_ci NOT NULL,
- UNIQUE KEY `id_command` (`id`,`command`),
- KEY `id` (`id`)
-) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-/*!40101 SET character_set_client = @saved_cs_client */;
-
---
--- Table structure for table `aowow_emotes_sounds`
---
-
-DROP TABLE IF EXISTS `aowow_emotes_sounds`;
-/*!40101 SET @saved_cs_client = @@character_set_client */;
-/*!40101 SET character_set_client = utf8 */;
-CREATE TABLE `aowow_emotes_sounds` (
- `emoteId` smallint(5) unsigned NOT NULL,
- `raceId` tinyint(3) unsigned NOT NULL,
- `gender` tinyint(1) unsigned NOT NULL,
- `soundId` smallint(5) unsigned NOT NULL,
- UNIQUE KEY `emoteId_raceId_gender_soundId` (`emoteId`,`raceId`,`gender`,`soundId`),
- KEY `emoteId` (`emoteId`),
- KEY `raceId` (`raceId`),
- KEY `soundId` (`soundId`)
-) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-/*!40101 SET character_set_client = @saved_cs_client */;
-
---
--- Table structure for table `aowow_errors`
---
-
-DROP TABLE IF EXISTS `aowow_errors`;
-/*!40101 SET @saved_cs_client = @@character_set_client */;
-/*!40101 SET character_set_client = utf8 */;
-CREATE TABLE `aowow_errors` (
- `date` int(10) unsigned DEFAULT NULL,
- `version` tinyint(3) unsigned NOT NULL,
- `phpError` smallint(5) unsigned NOT NULL,
- `file` varchar(150) COLLATE utf8mb4_unicode_ci NOT NULL,
- `line` smallint(5) unsigned NOT NULL,
- `query` varchar(250) COLLATE utf8mb4_unicode_ci NOT NULL,
- `userGroups` smallint(5) unsigned NOT NULL,
- `message` mediumtext COLLATE utf8mb4_unicode_ci DEFAULT NULL,
- PRIMARY KEY (`file`,`line`,`phpError`,`version`,`userGroups`)
-) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-/*!40101 SET character_set_client = @saved_cs_client */;
-
---
--- Table structure for table `aowow_events`
---
-
-DROP TABLE IF EXISTS `aowow_events`;
-/*!40101 SET @saved_cs_client = @@character_set_client */;
-/*!40101 SET character_set_client = utf8 */;
-CREATE TABLE `aowow_events` (
- `id` tinyint(3) unsigned NOT NULL,
- `holidayId` mediumint(8) unsigned NOT NULL DEFAULT 0,
- `cuFlags` int(10) unsigned NOT NULL DEFAULT 0,
- `startTime` bigint(20) NOT NULL,
- `endTime` bigint(20) NOT NULL,
- `occurence` bigint(20) unsigned NOT NULL,
- `length` bigint(20) unsigned NOT NULL,
- `requires` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
- `description` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
- PRIMARY KEY (`id`),
- KEY `holidayId` (`holidayId`)
-) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-/*!40101 SET character_set_client = @saved_cs_client */;
-
---
--- Table structure for table `aowow_factions`
---
-
-DROP TABLE IF EXISTS `aowow_factions`;
-/*!40101 SET @saved_cs_client = @@character_set_client */;
-/*!40101 SET character_set_client = utf8 */;
-CREATE TABLE `aowow_factions` (
- `id` smallint(5) unsigned NOT NULL,
- `repIdx` smallint(5) unsigned NOT NULL,
- `baseRepRaceMask1` smallint(5) unsigned NOT NULL,
- `baseRepRaceMask2` smallint(5) unsigned NOT NULL,
- `baseRepRaceMask3` smallint(5) unsigned NOT NULL,
- `baseRepRaceMask4` smallint(5) unsigned NOT NULL,
- `baseRepClassMask1` smallint(5) unsigned NOT NULL,
- `baseRepClassMask2` smallint(5) unsigned NOT NULL,
- `baseRepClassMask3` smallint(5) unsigned NOT NULL,
- `baseRepClassMask4` smallint(5) unsigned NOT NULL,
- `baseRepValue1` mediumint(8) NOT NULL,
- `baseRepValue2` mediumint(8) NOT NULL,
- `baseRepValue4` mediumint(8) NOT NULL,
- `baseRepValue3` mediumint(8) NOT NULL,
- `side` tinyint(1) unsigned NOT NULL,
- `expansion` tinyint(1) unsigned NOT NULL,
- `qmNpcIds` varchar(12) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT 'space separated',
- `templateIds` text COLLATE utf8mb4_unicode_ci NOT NULL COMMENT 'space separated',
- `cuFlags` int(10) unsigned NOT NULL,
- `parentFactionId` smallint(5) unsigned NOT NULL,
- `spilloverRateIn` float(8,2) NOT NULL,
- `spilloverRateOut` float(8,2) NOT NULL,
- `spilloverMaxRank` tinyint(3) unsigned NOT NULL,
- `name_loc0` varchar(35) COLLATE utf8mb4_unicode_ci NOT NULL,
- `name_loc2` varchar(49) COLLATE utf8mb4_unicode_ci NOT NULL,
- `name_loc3` varchar(40) COLLATE utf8mb4_unicode_ci NOT NULL,
- `name_loc4` varchar(40) COLLATE utf8mb4_unicode_ci NOT NULL,
- `name_loc6` varchar(50) COLLATE utf8mb4_unicode_ci NOT NULL,
- `name_loc8` varchar(47) COLLATE utf8mb4_unicode_ci NOT NULL,
- PRIMARY KEY (`id`)
-) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-/*!40101 SET character_set_client = @saved_cs_client */;
-
---
--- Table structure for table `aowow_factiontemplate`
---
-
-DROP TABLE IF EXISTS `aowow_factiontemplate`;
-/*!40101 SET @saved_cs_client = @@character_set_client */;
-/*!40101 SET character_set_client = utf8 */;
-CREATE TABLE `aowow_factiontemplate` (
- `id` smallint(5) unsigned NOT NULL,
- `factionId` smallint(5) unsigned NOT NULL,
- `A` tinyint(4) NOT NULL COMMENT 'Aliance: -1 - hostile, 1 - friendly, 0 - neutral',
- `H` tinyint(4) NOT NULL COMMENT 'Horde: -1 - hostile, 1 - friendly, 0 - neutral',
- PRIMARY KEY (`id`)
-) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-/*!40101 SET character_set_client = @saved_cs_client */;
-
---
--- Table structure for table `aowow_glyphproperties`
---
-
-DROP TABLE IF EXISTS `aowow_glyphproperties`;
-/*!40101 SET @saved_cs_client = @@character_set_client */;
-/*!40101 SET character_set_client = utf8 */;
-CREATE TABLE `aowow_glyphproperties` (
- `id` smallint(5) unsigned NOT NULL,
- `spellId` mediumint(11) unsigned NOT NULL,
- `typeFlags` tinyint(3) unsigned NOT NULL,
- `iconId` smallint(5) unsigned NOT NULL DEFAULT 0,
- `iconIdBak` smallint(5) unsigned NOT NULL DEFAULT 0,
- PRIMARY KEY (`id`)
-) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-/*!40101 SET character_set_client = @saved_cs_client */;
-
---
--- Table structure for table `aowow_guides`
---
-
-DROP TABLE IF EXISTS `aowow_guides`;
-/*!40101 SET @saved_cs_client = @@character_set_client */;
-/*!40101 SET character_set_client = utf8 */;
-CREATE TABLE `aowow_guides` (
- `id` mediumint unsigned NOT NULL AUTO_INCREMENT,
- `category` smallint unsigned NOT NULL DEFAULT '0',
- `classId` tinyint unsigned DEFAULT NULL,
- `specId` tinyint DEFAULT NULL,
- `title` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT 'title for menus + lists',
- `name` varchar(100) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT 'title for the page tiself',
- `description` varchar(200) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '',
- `url` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
- `locale` tinyint unsigned NOT NULL DEFAULT '0',
- `status` tinyint unsigned NOT NULL DEFAULT '1',
- `rev` tinyint unsigned NOT NULL DEFAULT '0',
- `cuFlags` int unsigned NOT NULL DEFAULT '0',
- `roles` smallint unsigned NOT NULL DEFAULT '0',
- `views` mediumint unsigned NOT NULL DEFAULT '0',
- `userId` mediumint unsigned DEFAULT NULL,
- `date` int unsigned NOT NULL DEFAULT '0',
- `approveUserId` mediumint unsigned DEFAULT NULL,
- `approveDate` int unsigned NOT NULL DEFAULT '0',
- `deleteUserId` mediumint unsigned DEFAULT NULL,
- `deleteData` int unsigned NOT NULL DEFAULT '0',
- PRIMARY KEY (`id`)
-) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
-/*!40101 SET character_set_client = @saved_cs_client */;
-
---
--- Table structure for table `aowow_guides_changelog`
---
-
-DROP TABLE IF EXISTS `aowow_guides_changelog`;
-/*!40101 SET @saved_cs_client = @@character_set_client */;
-/*!40101 SET character_set_client = utf8 */;
-CREATE TABLE `aowow_guides_changelog` (
- `id` mediumint unsigned NOT NULL,
- `rev` tinyint unsigned DEFAULT NULL,
- `date` int unsigned NOT NULL,
- `userId` mediumint unsigned NOT NULL,
- `status` tinyint unsigned NOT NULL DEFAULT '0',
- `msg` varchar(200) COLLATE utf8mb4_general_ci DEFAULT '',
- KEY `id` (`id`) USING BTREE
-) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
-/*!40101 SET character_set_client = @saved_cs_client */;
-
---
--- Table structure for table `aowow_holidays`
---
-
-DROP TABLE IF EXISTS `aowow_holidays`;
-/*!40101 SET @saved_cs_client = @@character_set_client */;
-/*!40101 SET character_set_client = utf8 */;
-CREATE TABLE `aowow_holidays` (
- `id` smallint(6) unsigned NOT NULL,
- `bossCreature` mediumint(8) unsigned NOT NULL,
- `achievementCatOrId` mediumint(9) NOT NULL,
- `name_loc0` varchar(36) COLLATE utf8mb4_unicode_ci NOT NULL,
- `name_loc2` varchar(42) COLLATE utf8mb4_unicode_ci NOT NULL,
- `name_loc3` varchar(36) COLLATE utf8mb4_unicode_ci NOT NULL,
- `name_loc4` varchar(36) COLLATE utf8mb4_unicode_ci NOT NULL,
- `name_loc6` varchar(49) COLLATE utf8mb4_unicode_ci NOT NULL,
- `name_loc8` varchar(29) COLLATE utf8mb4_unicode_ci NOT NULL,
- `description_loc0` mediumtext COLLATE utf8mb4_unicode_ci DEFAULT NULL,
- `description_loc2` mediumtext COLLATE utf8mb4_unicode_ci DEFAULT NULL,
- `description_loc3` mediumtext COLLATE utf8mb4_unicode_ci DEFAULT NULL,
- `description_loc4` mediumtext COLLATE utf8mb4_unicode_ci DEFAULT NULL,
- `description_loc6` mediumtext COLLATE utf8mb4_unicode_ci DEFAULT NULL,
- `description_loc8` mediumtext COLLATE utf8mb4_unicode_ci DEFAULT NULL,
- `looping` tinyint(2) NOT NULL,
- `scheduleType` tinyint(2) NOT NULL,
- `textureString` varchar(30) COLLATE utf8mb4_unicode_ci NOT NULL,
- `iconString` varchar(51) COLLATE utf8mb4_unicode_ci NOT NULL,
- PRIMARY KEY (`id`)
-) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-/*!40101 SET character_set_client = @saved_cs_client */;
-
---
--- Table structure for table `aowow_home_featuredbox`
---
-
-DROP TABLE IF EXISTS `aowow_home_featuredbox`;
-/*!40101 SET @saved_cs_client = @@character_set_client */;
-/*!40101 SET character_set_client = utf8 */;
-CREATE TABLE `aowow_home_featuredbox` (
- `id` smallint(5) unsigned NOT NULL,
- `editorId` int(10) unsigned DEFAULT NULL,
- `editDate` int(10) unsigned NOT NULL,
- `startDate` int(10) unsigned NOT NULL DEFAULT 0,
- `endDate` int(10) unsigned NOT NULL DEFAULT 0,
- `extraWide` tinyint(3) unsigned NOT NULL DEFAULT 0,
- `boxBG` varchar(150) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
- `altHomeLogo` varchar(150) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
- `altHeaderLogo` varchar(150) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
- `text_loc0` mediumtext COLLATE utf8mb4_unicode_ci NOT NULL,
- `text_loc2` mediumtext COLLATE utf8mb4_unicode_ci NOT NULL,
- `text_loc3` mediumtext COLLATE utf8mb4_unicode_ci NOT NULL,
- `text_loc4` mediumtext COLLATE utf8mb4_unicode_ci NOT NULL,
- `text_loc6` mediumtext COLLATE utf8mb4_unicode_ci NOT NULL,
- `text_loc8` mediumtext COLLATE utf8mb4_unicode_ci NOT NULL,
- PRIMARY KEY (`id`),
- KEY `FK_acc_hFBox` (`editorId`),
- CONSTRAINT `FK_acc_hFBox` FOREIGN KEY (`editorId`) REFERENCES `aowow_account` (`id`) ON DELETE SET NULL ON UPDATE CASCADE
-) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-/*!40101 SET character_set_client = @saved_cs_client */;
-
---
--- Table structure for table `aowow_home_featuredbox_overlay`
---
-
-DROP TABLE IF EXISTS `aowow_home_featuredbox_overlay`;
-/*!40101 SET @saved_cs_client = @@character_set_client */;
-/*!40101 SET character_set_client = utf8 */;
-CREATE TABLE `aowow_home_featuredbox_overlay` (
- `featureId` smallint(5) unsigned NOT NULL,
- `left` smallint(5) unsigned NOT NULL,
- `width` smallint(5) unsigned NOT NULL,
- `url` varchar(150) COLLATE utf8mb4_unicode_ci NOT NULL,
- `title_loc0` varchar(100) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '',
- `title_loc2` varchar(100) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '',
- `title_loc3` varchar(100) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '',
- `title_loc4` varchar(100) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '',
- `title_loc6` varchar(100) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '',
- `title_loc8` varchar(100) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '',
- KEY `FK_home_featurebox` (`featureId`),
- CONSTRAINT `FK_home_featurebox` FOREIGN KEY (`featureId`) REFERENCES `aowow_home_featuredbox` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
-) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-/*!40101 SET character_set_client = @saved_cs_client */;
-
---
--- Table structure for table `aowow_home_oneliner`
---
-
-DROP TABLE IF EXISTS `aowow_home_oneliner`;
-/*!40101 SET @saved_cs_client = @@character_set_client */;
-/*!40101 SET character_set_client = utf8 */;
-CREATE TABLE `aowow_home_oneliner` (
- `id` smallint(5) unsigned NOT NULL,
- `editorId` int(10) unsigned DEFAULT NULL,
- `editDate` int(10) unsigned NOT NULL,
- `active` tinyint(1) unsigned NOT NULL,
- `text_loc0` varchar(200) COLLATE utf8mb4_unicode_ci NOT NULL,
- `text_loc2` varchar(200) COLLATE utf8mb4_unicode_ci NOT NULL,
- `text_loc3` varchar(200) COLLATE utf8mb4_unicode_ci NOT NULL,
- `text_loc4` varchar(200) COLLATE utf8mb4_unicode_ci NOT NULL,
- `text_loc6` varchar(200) COLLATE utf8mb4_unicode_ci NOT NULL,
- `text_loc8` varchar(200) COLLATE utf8mb4_unicode_ci NOT NULL,
- PRIMARY KEY (`id`),
- KEY `FK_acc_hOneliner` (`editorId`),
- CONSTRAINT `FK_acc_hOneliner` FOREIGN KEY (`editorId`) REFERENCES `aowow_account` (`id`) ON DELETE SET NULL ON UPDATE CASCADE
-) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-/*!40101 SET character_set_client = @saved_cs_client */;
-
---
--- Table structure for table `aowow_home_titles`
---
-
-DROP TABLE IF EXISTS `aowow_home_titles`;
-/*!40101 SET @saved_cs_client = @@character_set_client */;
-/*!40101 SET character_set_client = utf8 */;
-CREATE TABLE `aowow_home_titles` (
- `id` smallint(5) unsigned NOT NULL AUTO_INCREMENT,
- `editorId` int(10) unsigned DEFAULT NULL,
- `editDate` int(10) unsigned NOT NULL,
- `active` tinyint(1) unsigned NOT NULL,
- `locale` tinyint(3) unsigned NOT NULL,
- `title` varchar(100) COLLATE utf8mb4_unicode_ci NOT NULL,
- PRIMARY KEY (`id`),
- UNIQUE KEY `locale_title` (`locale`,`title`),
- KEY `FK_acc_hTitles` (`editorId`),
- CONSTRAINT `FK_acc_hTitles` FOREIGN KEY (`editorId`) REFERENCES `aowow_account` (`id`) ON DELETE SET NULL ON UPDATE CASCADE
-) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-/*!40101 SET character_set_client = @saved_cs_client */;
-
---
--- Table structure for table `aowow_icons`
---
-
-DROP TABLE IF EXISTS `aowow_icons`;
-/*!40101 SET @saved_cs_client = @@character_set_client */;
-/*!40101 SET character_set_client = utf8 */;
-CREATE TABLE `aowow_icons` (
- `id` smallint(5) unsigned NOT NULL AUTO_INCREMENT,
- `cuFlags` int(11) unsigned NOT NULL DEFAULT 0,
- `name` varchar(55) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '',
- PRIMARY KEY (`id`),
- KEY `name` (`name`)
-) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-/*!40101 SET character_set_client = @saved_cs_client */;
-
---
--- Table structure for table `aowow_item_stats`
---
-
-DROP TABLE IF EXISTS `aowow_item_stats`;
-/*!40101 SET @saved_cs_client = @@character_set_client */;
-/*!40101 SET character_set_client = utf8 */;
-CREATE TABLE `aowow_item_stats` (
- `type` smallint(5) unsigned NOT NULL,
- `typeId` mediumint(8) unsigned NOT NULL,
- `nsockets` tinyint(3) unsigned NOT NULL,
- `dmgmin1` smallint(5) unsigned NOT NULL,
- `dmgmax1` smallint(5) unsigned NOT NULL,
- `speed` float(8,2) NOT NULL,
- `dps` float(8,2) NOT NULL,
- `mledmgmin` smallint(5) unsigned NOT NULL,
- `mledmgmax` smallint(5) unsigned NOT NULL,
- `mlespeed` float(8,2) NOT NULL,
- `mledps` float(8,2) NOT NULL,
- `rgddmgmin` smallint(5) unsigned NOT NULL,
- `rgddmgmax` smallint(5) unsigned NOT NULL,
- `rgdspeed` float(8,2) NOT NULL,
- `rgddps` float(8,2) NOT NULL,
- `dmg` float(8,2) NOT NULL,
- `damagetype` tinyint(4) NOT NULL,
- `mana` smallint(6) NOT NULL,
- `health` smallint(6) NOT NULL,
- `agi` smallint(6) NOT NULL,
- `str` smallint(6) NOT NULL,
- `int` smallint(6) NOT NULL,
- `spi` smallint(6) NOT NULL,
- `sta` smallint(6) NOT NULL,
- `energy` smallint(6) NOT NULL,
- `rage` smallint(6) NOT NULL,
- `focus` smallint(6) NOT NULL,
- `runicpwr` smallint(6) NOT NULL,
- `defrtng` smallint(6) NOT NULL,
- `dodgertng` smallint(6) NOT NULL,
- `parryrtng` smallint(6) NOT NULL,
- `blockrtng` smallint(6) NOT NULL,
- `mlehitrtng` smallint(6) NOT NULL,
- `rgdhitrtng` smallint(6) NOT NULL,
- `splhitrtng` smallint(6) NOT NULL,
- `mlecritstrkrtng` smallint(6) NOT NULL,
- `rgdcritstrkrtng` smallint(6) NOT NULL,
- `splcritstrkrtng` smallint(6) NOT NULL,
- `_mlehitrtng` smallint(6) NOT NULL,
- `_rgdhitrtng` smallint(6) NOT NULL,
- `_splhitrtng` smallint(6) NOT NULL,
- `_mlecritstrkrtng` smallint(6) NOT NULL,
- `_rgdcritstrkrtng` smallint(6) NOT NULL,
- `_splcritstrkrtng` smallint(6) NOT NULL,
- `mlehastertng` smallint(6) NOT NULL,
- `rgdhastertng` smallint(6) NOT NULL,
- `splhastertng` smallint(6) NOT NULL,
- `hitrtng` smallint(6) NOT NULL,
- `critstrkrtng` smallint(6) NOT NULL,
- `_hitrtng` smallint(6) NOT NULL,
- `_critstrkrtng` smallint(6) NOT NULL,
- `resirtng` smallint(6) NOT NULL,
- `hastertng` smallint(6) NOT NULL,
- `exprtng` smallint(6) NOT NULL,
- `atkpwr` smallint(6) NOT NULL,
- `mleatkpwr` smallint(6) NOT NULL,
- `rgdatkpwr` smallint(6) NOT NULL,
- `feratkpwr` smallint(6) NOT NULL,
- `splheal` smallint(6) NOT NULL,
- `spldmg` smallint(6) NOT NULL,
- `manargn` smallint(6) NOT NULL,
- `armorpenrtng` smallint(6) NOT NULL,
- `splpwr` smallint(6) NOT NULL,
- `healthrgn` smallint(6) NOT NULL,
- `splpen` smallint(6) NOT NULL,
- `block` smallint(6) NOT NULL,
- `mastrtng` smallint(6) NOT NULL,
- `armor` smallint(6) NOT NULL,
- `armorbonus` smallint(6) NOT NULL,
- `firres` smallint(6) NOT NULL,
- `frores` smallint(6) NOT NULL,
- `holres` smallint(6) NOT NULL,
- `shares` smallint(6) NOT NULL,
- `natres` smallint(6) NOT NULL,
- `arcres` smallint(6) NOT NULL,
- `firsplpwr` smallint(6) NOT NULL,
- `frosplpwr` smallint(6) NOT NULL,
- `holsplpwr` smallint(6) NOT NULL,
- `shasplpwr` smallint(6) NOT NULL,
- `natsplpwr` smallint(6) NOT NULL,
- `arcsplpwr` smallint(6) NOT NULL,
- PRIMARY KEY (`typeId`,`type`)
-) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-/*!40101 SET character_set_client = @saved_cs_client */;
-
---
--- Table structure for table `aowow_itemenchantment`
---
-
-DROP TABLE IF EXISTS `aowow_itemenchantment`;
-/*!40101 SET @saved_cs_client = @@character_set_client */;
-/*!40101 SET character_set_client = utf8 */;
-CREATE TABLE `aowow_itemenchantment` (
- `id` smallint(5) unsigned NOT NULL,
- `charges` tinyint(4) unsigned NOT NULL,
- `cuFlags` int(10) unsigned NOT NULL,
- `procChance` tinyint(3) unsigned NOT NULL,
- `ppmRate` float NOT NULL,
- `type1` tinyint(4) unsigned NOT NULL,
- `type2` tinyint(4) unsigned NOT NULL,
- `type3` tinyint(4) unsigned NOT NULL,
- `amount1` smallint(6) NOT NULL,
- `amount2` smallint(6) NOT NULL,
- `amount3` smallint(6) NOT NULL,
- `object1` mediumint(9) unsigned NOT NULL,
- `object2` mediumint(9) unsigned NOT NULL,
- `object3` smallint(6) unsigned NOT NULL,
- `name_loc0` varchar(65) COLLATE utf8mb4_unicode_ci NOT NULL,
- `name_loc2` varchar(91) COLLATE utf8mb4_unicode_ci NOT NULL,
- `name_loc3` varchar(84) COLLATE utf8mb4_unicode_ci NOT NULL,
- `name_loc4` varchar(84) COLLATE utf8mb4_unicode_ci NOT NULL,
- `name_loc6` varchar(89) COLLATE utf8mb4_unicode_ci NOT NULL,
- `name_loc8` varchar(96) COLLATE utf8mb4_unicode_ci NOT NULL,
- `conditionId` tinyint(3) unsigned NOT NULL,
- `skillLine` smallint(5) unsigned NOT NULL,
- `skillLevel` smallint(5) unsigned NOT NULL,
- `requiredLevel` tinyint(3) unsigned NOT NULL,
- PRIMARY KEY (`id`)
-) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-/*!40101 SET character_set_client = @saved_cs_client */;
-
---
--- Table structure for table `aowow_itemenchantmentcondition`
---
-
-DROP TABLE IF EXISTS `aowow_itemenchantmentcondition`;
-/*!40101 SET @saved_cs_client = @@character_set_client */;
-/*!40101 SET character_set_client = utf8 */;
-CREATE TABLE `aowow_itemenchantmentcondition` (
- `id` smallint(6) unsigned NOT NULL,
- `color1` tinyint(4) unsigned zerofill NOT NULL,
- `color2` tinyint(4) unsigned zerofill NOT NULL,
- `color3` tinyint(4) unsigned zerofill NOT NULL,
- `color4` tinyint(4) unsigned zerofill NOT NULL,
- `color5` tinyint(4) unsigned zerofill NOT NULL,
- `comparator1` tinyint(4) unsigned zerofill NOT NULL,
- `comparator2` tinyint(4) unsigned zerofill NOT NULL,
- `comparator3` tinyint(4) unsigned zerofill NOT NULL,
- `comparator4` tinyint(4) unsigned zerofill NOT NULL,
- `comparator5` tinyint(4) unsigned zerofill NOT NULL,
- `cmpColor1` tinyint(4) unsigned zerofill NOT NULL,
- `cmpColor2` tinyint(4) unsigned zerofill NOT NULL,
- `cmpColor3` tinyint(4) unsigned zerofill NOT NULL,
- `cmpColor4` tinyint(4) unsigned zerofill NOT NULL,
- `cmpColor5` tinyint(4) unsigned zerofill NOT NULL,
- `value1` tinyint(4) unsigned zerofill NOT NULL,
- `value2` tinyint(4) unsigned zerofill NOT NULL,
- `value3` tinyint(4) unsigned zerofill NOT NULL,
- `value4` tinyint(4) unsigned zerofill NOT NULL,
- `value5` tinyint(4) unsigned zerofill NOT NULL,
- PRIMARY KEY (`id`)
-) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-/*!40101 SET character_set_client = @saved_cs_client */;
-
---
--- Table structure for table `aowow_itemextendedcost`
---
-
-DROP TABLE IF EXISTS `aowow_itemextendedcost`;
-/*!40101 SET @saved_cs_client = @@character_set_client */;
-/*!40101 SET character_set_client = utf8 */;
-CREATE TABLE `aowow_itemextendedcost` (
- `id` smallint(5) unsigned NOT NULL,
- `reqHonorPoints` mediumint(8) unsigned NOT NULL,
- `reqArenaPoints` smallint(5) unsigned NOT NULL,
- `reqArenaSlot` tinyint(3) unsigned NOT NULL,
- `reqItemId1` mediumint(8) unsigned NOT NULL,
- `reqItemId2` mediumint(8) unsigned NOT NULL,
- `reqItemId3` mediumint(8) unsigned NOT NULL,
- `reqItemId4` mediumint(8) unsigned NOT NULL,
- `reqItemId5` mediumint(8) unsigned NOT NULL,
- `itemCount1` smallint(5) unsigned NOT NULL,
- `itemCount2` smallint(5) unsigned NOT NULL,
- `itemCount3` smallint(5) unsigned NOT NULL,
- `itemCount4` smallint(5) unsigned NOT NULL,
- `itemCount5` smallint(5) unsigned NOT NULL,
- `reqPersonalRating` smallint(5) unsigned NOT NULL,
- PRIMARY KEY (`id`)
-) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-/*!40101 SET character_set_client = @saved_cs_client */;
-
---
--- Table structure for table `aowow_itemlimitcategory`
---
-
-DROP TABLE IF EXISTS `aowow_itemlimitcategory`;
-/*!40101 SET @saved_cs_client = @@character_set_client */;
-/*!40101 SET character_set_client = utf8 */;
-CREATE TABLE `aowow_itemlimitcategory` (
- `id` tinyint(3) unsigned NOT NULL,
- `name_loc0` varchar(31) COLLATE utf8mb4_unicode_ci NOT NULL,
- `name_loc2` varchar(36) COLLATE utf8mb4_unicode_ci NOT NULL,
- `name_loc3` varchar(34) COLLATE utf8mb4_unicode_ci NOT NULL,
- `name_loc4` varchar(34) COLLATE utf8mb4_unicode_ci NOT NULL,
- `name_loc6` varchar(40) COLLATE utf8mb4_unicode_ci NOT NULL,
- `name_loc8` varchar(35) COLLATE utf8mb4_unicode_ci NOT NULL,
- `count` tinyint(3) unsigned NOT NULL,
- `isGem` tinyint(3) unsigned NOT NULL,
- PRIMARY KEY (`id`)
-) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-/*!40101 SET character_set_client = @saved_cs_client */;
-
---
--- Table structure for table `aowow_itemrandomenchant`
---
-
-DROP TABLE IF EXISTS `aowow_itemrandomenchant`;
-/*!40101 SET @saved_cs_client = @@character_set_client */;
-/*!40101 SET character_set_client = utf8 */;
-CREATE TABLE `aowow_itemrandomenchant` (
- `id` smallint(6) NOT NULL,
- `name_loc0` varchar(250) COLLATE utf8mb4_unicode_ci NOT NULL,
- `name_loc2` varchar(250) COLLATE utf8mb4_unicode_ci NOT NULL,
- `name_loc3` varchar(250) COLLATE utf8mb4_unicode_ci NOT NULL,
- `name_loc4` varchar(250) COLLATE utf8mb4_unicode_ci NOT NULL,
- `name_loc6` varchar(250) COLLATE utf8mb4_unicode_ci NOT NULL,
- `name_loc8` varchar(250) COLLATE utf8mb4_unicode_ci NOT NULL,
- `nameINT` char(250) COLLATE utf8mb4_unicode_ci NOT NULL,
- `enchantId1` smallint(5) unsigned NOT NULL,
- `enchantId2` smallint(5) unsigned NOT NULL,
- `enchantId3` smallint(5) unsigned NOT NULL,
- `enchantId4` smallint(5) unsigned NOT NULL,
- `enchantId5` smallint(5) unsigned NOT NULL,
- `allocationPct1` smallint(5) unsigned NOT NULL,
- `allocationPct2` smallint(5) unsigned NOT NULL,
- `allocationPct3` smallint(5) unsigned NOT NULL,
- `allocationPct4` smallint(5) unsigned NOT NULL,
- `allocationPct5` smallint(5) unsigned NOT NULL,
- PRIMARY KEY (`id`)
-) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-/*!40101 SET character_set_client = @saved_cs_client */;
-
---
--- Table structure for table `aowow_itemrandomproppoints`
---
-
-DROP TABLE IF EXISTS `aowow_itemrandomproppoints`;
-/*!40101 SET @saved_cs_client = @@character_set_client */;
-/*!40101 SET character_set_client = utf8 */;
-CREATE TABLE `aowow_itemrandomproppoints` (
- `id` smallint(5) unsigned NOT NULL,
- `epic1` smallint(5) unsigned NOT NULL,
- `epic2` smallint(5) unsigned NOT NULL,
- `epic3` smallint(5) unsigned NOT NULL,
- `epic4` smallint(5) unsigned NOT NULL,
- `epic5` smallint(5) unsigned NOT NULL,
- `rare1` smallint(5) unsigned NOT NULL,
- `rare2` smallint(5) unsigned NOT NULL,
- `rare3` smallint(5) unsigned NOT NULL,
- `rare4` smallint(5) unsigned NOT NULL,
- `rare5` smallint(5) unsigned NOT NULL,
- `uncommon1` smallint(5) unsigned NOT NULL,
- `uncommon2` smallint(5) unsigned NOT NULL,
- `uncommon3` smallint(5) unsigned NOT NULL,
- `uncommon4` smallint(5) unsigned NOT NULL,
- `uncommon5` smallint(5) unsigned NOT NULL,
- PRIMARY KEY (`id`)
-) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-/*!40101 SET character_set_client = @saved_cs_client */;
-
---
--- Table structure for table `aowow_items`
---
-
-DROP TABLE IF EXISTS `aowow_items`;
-/*!40101 SET @saved_cs_client = @@character_set_client */;
-/*!40101 SET character_set_client = utf8 */;
-CREATE TABLE `aowow_items` (
- `id` mediumint(8) unsigned NOT NULL DEFAULT 0,
- `class` tinyint(3) unsigned NOT NULL DEFAULT 0,
- `classBak` tinyint(3) NOT NULL,
- `subClass` tinyint(3) NOT NULL DEFAULT 0,
- `subClassBak` tinyint(3) NOT NULL,
- `soundOverrideSubclass` tinyint(3) NOT NULL,
- `subSubClass` tinyint(3) NOT NULL,
- `name_loc0` varchar(127) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '',
- `name_loc2` varchar(127) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
- `name_loc3` varchar(127) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
- `name_loc4` varchar(127) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
- `name_loc6` varchar(127) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
- `name_loc8` varchar(127) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
- `iconId` smallint(5) unsigned NOT NULL DEFAULT 0,
- `displayId` mediumint(8) unsigned NOT NULL DEFAULT 0,
- `spellVisualId` smallint(5) unsigned NOT NULL DEFAULT 0,
- `quality` tinyint(3) unsigned NOT NULL DEFAULT 0,
- `flags` bigint(20) NOT NULL DEFAULT 0,
- `flagsExtra` int(10) unsigned NOT NULL DEFAULT 0,
- `buyCount` tinyint(3) unsigned NOT NULL DEFAULT 1,
- `buyPrice` bigint(20) NOT NULL DEFAULT 0,
- `sellPrice` int(10) unsigned NOT NULL DEFAULT 0,
- `repairPrice` int(10) unsigned NOT NULL,
- `slot` tinyint(3) NOT NULL,
- `slotBak` tinyint(3) unsigned NOT NULL DEFAULT 0,
- `requiredClass` int(11) NOT NULL DEFAULT -1,
- `requiredRace` int(11) NOT NULL DEFAULT -1,
- `itemLevel` smallint(5) unsigned NOT NULL DEFAULT 0,
- `requiredLevel` tinyint(3) unsigned NOT NULL DEFAULT 0,
- `requiredSkill` smallint(5) unsigned NOT NULL DEFAULT 0,
- `requiredSkillRank` smallint(5) unsigned NOT NULL DEFAULT 0,
- `requiredSpell` mediumint(8) unsigned NOT NULL DEFAULT 0,
- `requiredHonorRank` mediumint(8) unsigned NOT NULL DEFAULT 0,
- `requiredCityRank` mediumint(8) unsigned NOT NULL DEFAULT 0,
- `requiredFaction` smallint(5) unsigned NOT NULL DEFAULT 0,
- `requiredFactionRank` smallint(5) unsigned NOT NULL DEFAULT 0,
- `maxCount` int(11) NOT NULL DEFAULT 0,
- `cuFlags` int(10) unsigned NOT NULL,
- `model` varchar(50) COLLATE utf8mb4_unicode_ci NOT NULL,
- `stackable` int(11) DEFAULT 1,
- `slots` tinyint(3) unsigned NOT NULL DEFAULT 0,
- `statType1` tinyint(3) unsigned NOT NULL DEFAULT 0,
- `statValue1` smallint(6) NOT NULL DEFAULT 0,
- `statType2` tinyint(3) unsigned NOT NULL DEFAULT 0,
- `statValue2` smallint(6) NOT NULL DEFAULT 0,
- `statType3` tinyint(3) unsigned NOT NULL DEFAULT 0,
- `statValue3` smallint(6) NOT NULL DEFAULT 0,
- `statType4` tinyint(3) unsigned NOT NULL DEFAULT 0,
- `statValue4` smallint(6) NOT NULL DEFAULT 0,
- `statType5` tinyint(3) unsigned NOT NULL DEFAULT 0,
- `statValue5` smallint(6) NOT NULL DEFAULT 0,
- `statType6` tinyint(3) unsigned NOT NULL DEFAULT 0,
- `statValue6` smallint(6) NOT NULL DEFAULT 0,
- `statType7` tinyint(3) unsigned NOT NULL DEFAULT 0,
- `statValue7` smallint(6) NOT NULL DEFAULT 0,
- `statType8` tinyint(3) unsigned NOT NULL DEFAULT 0,
- `statValue8` smallint(6) NOT NULL DEFAULT 0,
- `statType9` tinyint(3) unsigned NOT NULL DEFAULT 0,
- `statValue9` smallint(6) NOT NULL DEFAULT 0,
- `statType10` tinyint(3) unsigned NOT NULL DEFAULT 0,
- `statValue10` smallint(6) NOT NULL DEFAULT 0,
- `scalingStatDistribution` smallint(6) NOT NULL DEFAULT 0,
- `scalingStatValue` int(10) unsigned NOT NULL DEFAULT 0,
- `dmgMin1` float NOT NULL DEFAULT 0,
- `dmgMax1` float NOT NULL DEFAULT 0,
- `dmgType1` tinyint(3) unsigned NOT NULL DEFAULT 0,
- `dmgMin2` float NOT NULL DEFAULT 0,
- `dmgMax2` float NOT NULL DEFAULT 0,
- `dmgType2` tinyint(3) unsigned NOT NULL DEFAULT 0,
- `delay` smallint(5) unsigned NOT NULL DEFAULT 1000,
- `armor` smallint(5) unsigned NOT NULL DEFAULT 0,
- `armorDamageModifier` float NOT NULL DEFAULT 0,
- `block` mediumint(8) unsigned NOT NULL DEFAULT 0,
- `resHoly` tinyint(3) unsigned NOT NULL DEFAULT 0,
- `resFire` tinyint(3) unsigned NOT NULL DEFAULT 0,
- `resNature` tinyint(3) unsigned NOT NULL DEFAULT 0,
- `resFrost` tinyint(3) unsigned NOT NULL DEFAULT 0,
- `resShadow` tinyint(3) unsigned NOT NULL DEFAULT 0,
- `resArcane` tinyint(3) unsigned NOT NULL DEFAULT 0,
- `ammoType` tinyint(3) unsigned NOT NULL DEFAULT 0,
- `rangedModRange` float NOT NULL DEFAULT 0,
- `spellId1` mediumint(8) NOT NULL DEFAULT 0,
- `spellTrigger1` tinyint(3) unsigned NOT NULL DEFAULT 0,
- `spellCharges1` smallint(6) DEFAULT NULL,
- `spellppmRate1` float NOT NULL DEFAULT 0,
- `spellCooldown1` int(11) NOT NULL DEFAULT -1,
- `spellCategory1` smallint(5) unsigned NOT NULL DEFAULT 0,
- `spellCategoryCooldown1` int(11) NOT NULL DEFAULT -1,
- `spellId2` mediumint(8) NOT NULL DEFAULT 0,
- `spellTrigger2` tinyint(3) unsigned NOT NULL DEFAULT 0,
- `spellCharges2` smallint(6) DEFAULT NULL,
- `spellppmRate2` float NOT NULL DEFAULT 0,
- `spellCooldown2` int(11) NOT NULL DEFAULT -1,
- `spellCategory2` smallint(5) unsigned NOT NULL DEFAULT 0,
- `spellCategoryCooldown2` int(11) NOT NULL DEFAULT -1,
- `spellId3` mediumint(8) NOT NULL DEFAULT 0,
- `spellTrigger3` tinyint(3) unsigned NOT NULL DEFAULT 0,
- `spellCharges3` smallint(6) DEFAULT NULL,
- `spellppmRate3` float NOT NULL DEFAULT 0,
- `spellCooldown3` int(11) NOT NULL DEFAULT -1,
- `spellCategory3` smallint(5) unsigned NOT NULL DEFAULT 0,
- `spellCategoryCooldown3` int(11) NOT NULL DEFAULT -1,
- `spellId4` mediumint(8) NOT NULL DEFAULT 0,
- `spellTrigger4` tinyint(3) unsigned NOT NULL DEFAULT 0,
- `spellCharges4` smallint(6) DEFAULT NULL,
- `spellppmRate4` float NOT NULL DEFAULT 0,
- `spellCooldown4` int(11) NOT NULL DEFAULT -1,
- `spellCategory4` smallint(5) unsigned NOT NULL DEFAULT 0,
- `spellCategoryCooldown4` int(11) NOT NULL DEFAULT -1,
- `spellId5` mediumint(8) NOT NULL DEFAULT 0,
- `spellTrigger5` tinyint(3) unsigned NOT NULL DEFAULT 0,
- `spellCharges5` smallint(6) DEFAULT NULL,
- `spellppmRate5` float NOT NULL DEFAULT 0,
- `spellCooldown5` int(11) NOT NULL DEFAULT -1,
- `spellCategory5` smallint(5) unsigned NOT NULL DEFAULT 0,
- `spellCategoryCooldown5` int(11) NOT NULL DEFAULT -1,
- `bonding` tinyint(3) unsigned NOT NULL DEFAULT 0,
- `description_loc0` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '',
- `description_loc2` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
- `description_loc3` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
- `description_loc4` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
- `description_loc6` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
- `description_loc8` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
- `pageTextId` mediumint(8) unsigned NOT NULL DEFAULT 0,
- `languageId` tinyint(3) unsigned NOT NULL DEFAULT 0,
- `startQuest` mediumint(8) unsigned NOT NULL DEFAULT 0,
- `lockId` mediumint(8) unsigned NOT NULL DEFAULT 0,
- `material` tinyint(3) NOT NULL DEFAULT 0,
- `randomEnchant` mediumint(8) NOT NULL DEFAULT 0,
- `itemset` mediumint(8) unsigned NOT NULL DEFAULT 0,
- `durability` smallint(5) unsigned NOT NULL DEFAULT 0,
- `area` mediumint(8) unsigned NOT NULL DEFAULT 0,
- `map` smallint(6) NOT NULL DEFAULT 0,
- `bagFamily` mediumint(8) NOT NULL DEFAULT 0,
- `totemCategory` mediumint(8) NOT NULL DEFAULT 0,
- `socketColor1` tinyint(4) NOT NULL DEFAULT 0,
- `socketContent1` mediumint(8) NOT NULL DEFAULT 0,
- `socketColor2` tinyint(4) NOT NULL DEFAULT 0,
- `socketContent2` mediumint(8) NOT NULL DEFAULT 0,
- `socketColor3` tinyint(4) NOT NULL DEFAULT 0,
- `socketContent3` mediumint(8) NOT NULL DEFAULT 0,
- `socketBonus` mediumint(8) NOT NULL DEFAULT 0,
- `gemColorMask` mediumint(8) NOT NULL DEFAULT 0,
- `requiredDisenchantSkill` smallint(6) NOT NULL DEFAULT -1,
- `disenchantId` mediumint(8) unsigned NOT NULL DEFAULT 0,
- `duration` int(10) unsigned NOT NULL DEFAULT 0,
- `itemLimitCategory` smallint(6) NOT NULL DEFAULT 0,
- `eventId` smallint(5) unsigned NOT NULL,
- `scriptName` varchar(64) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '',
- `foodType` tinyint(3) unsigned NOT NULL DEFAULT 0,
- `gemEnchantmentId` mediumint(8) NOT NULL,
- `minMoneyLoot` int(10) unsigned NOT NULL DEFAULT 0,
- `maxMoneyLoot` int(10) unsigned NOT NULL DEFAULT 0,
- `pickUpSoundId` smallint(5) unsigned NOT NULL DEFAULT 0,
- `dropDownSoundId` smallint(5) unsigned NOT NULL DEFAULT 0,
- `sheatheSoundId` smallint(5) unsigned NOT NULL DEFAULT 0,
- `unsheatheSoundId` smallint(5) unsigned NOT NULL DEFAULT 0,
- `flagsCustom` int(10) unsigned NOT NULL DEFAULT 0,
- PRIMARY KEY (`id`),
- KEY `idx_name` (`name_loc0`),
- KEY `items_index` (`class`),
- KEY `idx_model` (`displayId`),
- KEY `idx_faction` (`requiredFaction`),
- KEY `iconId` (`iconId`)
-) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-/*!40101 SET character_set_client = @saved_cs_client */;
-
---
--- Table structure for table `aowow_items_sounds`
---
-
-DROP TABLE IF EXISTS `aowow_items_sounds`;
-/*!40101 SET @saved_cs_client = @@character_set_client */;
-/*!40101 SET character_set_client = utf8 */;
-CREATE TABLE `aowow_items_sounds` (
- `soundId` smallint(5) unsigned NOT NULL,
- `subClassMask` mediumint(8) unsigned NOT NULL,
- PRIMARY KEY (`soundId`)
-) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='actually .. its only weapon related sounds in here';
-/*!40101 SET character_set_client = @saved_cs_client */;
-
---
--- Table structure for table `aowow_itemset`
---
-
-DROP TABLE IF EXISTS `aowow_itemset`;
-/*!40101 SET @saved_cs_client = @@character_set_client */;
-/*!40101 SET character_set_client = utf8 */;
-CREATE TABLE `aowow_itemset` (
- `id` int(16) NOT NULL,
- `refSetId` int(11) NOT NULL,
- `cuFlags` int(10) unsigned NOT NULL,
- `name_loc0` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
- `name_loc2` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
- `name_loc3` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
- `name_loc4` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
- `name_loc6` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
- `name_loc8` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
- `item1` mediumint(11) unsigned NOT NULL,
- `item2` mediumint(11) unsigned NOT NULL,
- `item3` mediumint(11) unsigned NOT NULL,
- `item4` mediumint(11) unsigned NOT NULL,
- `item5` mediumint(11) unsigned NOT NULL,
- `item6` mediumint(11) unsigned NOT NULL,
- `item7` mediumint(11) unsigned NOT NULL,
- `item8` mediumint(11) unsigned NOT NULL,
- `item9` mediumint(11) unsigned NOT NULL,
- `item10` mediumint(11) unsigned NOT NULL,
- `spell1` mediumint(11) unsigned NOT NULL,
- `spell2` mediumint(11) unsigned NOT NULL,
- `spell3` mediumint(11) unsigned NOT NULL,
- `spell4` mediumint(11) unsigned NOT NULL,
- `spell5` mediumint(11) unsigned NOT NULL,
- `spell6` mediumint(11) unsigned NOT NULL,
- `spell7` mediumint(11) unsigned NOT NULL,
- `spell8` mediumint(11) unsigned NOT NULL,
- `bonus1` tinyint(1) unsigned NOT NULL,
- `bonus2` tinyint(1) unsigned NOT NULL,
- `bonus3` tinyint(1) unsigned NOT NULL,
- `bonus4` tinyint(1) unsigned NOT NULL,
- `bonus5` tinyint(1) unsigned NOT NULL,
- `bonus6` tinyint(1) unsigned NOT NULL,
- `bonus7` tinyint(1) unsigned NOT NULL,
- `bonus8` tinyint(1) unsigned NOT NULL,
- `bonusText_loc0` varchar(256) COLLATE utf8mb4_unicode_ci NOT NULL,
- `bonusText_loc2` varchar(256) COLLATE utf8mb4_unicode_ci NOT NULL,
- `bonusText_loc3` varchar(256) COLLATE utf8mb4_unicode_ci NOT NULL,
- `bonusText_loc4` varchar(256) COLLATE utf8mb4_unicode_ci NOT NULL,
- `bonusText_loc6` varchar(256) COLLATE utf8mb4_unicode_ci NOT NULL,
- `bonusText_loc8` varchar(256) COLLATE utf8mb4_unicode_ci NOT NULL,
- `bonusParsed` varchar(256) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT 'serialized itemMods',
- `npieces` tinyint(3) NOT NULL,
- `minLevel` smallint(6) NOT NULL,
- `maxLevel` smallint(6) NOT NULL,
- `reqLevel` smallint(6) NOT NULL,
- `classMask` mediumint(9) NOT NULL,
- `heroic` tinyint(1) NOT NULL COMMENT 'bool',
- `quality` tinyint(4) NOT NULL,
- `type` smallint(6) NOT NULL COMMENT 'g_itemset_types',
- `contentGroup` smallint(6) NOT NULL COMMENT 'g_itemset_notes',
- `eventId` smallint(3) unsigned NOT NULL,
- `skillId` smallint(3) unsigned NOT NULL,
- `skillLevel` smallint(3) unsigned NOT NULL,
- PRIMARY KEY (`id`)
-) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-/*!40101 SET character_set_client = @saved_cs_client */;
-
---
--- Table structure for table `aowow_lock`
---
-
-DROP TABLE IF EXISTS `aowow_lock`;
-/*!40101 SET @saved_cs_client = @@character_set_client */;
-/*!40101 SET character_set_client = utf8 */;
-CREATE TABLE `aowow_lock` (
- `id` mediumint(8) unsigned NOT NULL,
- `type1` tinyint(3) unsigned NOT NULL,
- `type2` tinyint(3) unsigned NOT NULL,
- `type3` tinyint(3) unsigned NOT NULL,
- `type4` tinyint(3) unsigned NOT NULL,
- `type5` tinyint(3) unsigned NOT NULL,
- `properties1` smallint(5) unsigned NOT NULL,
- `properties2` mediumint(8) unsigned NOT NULL,
- `properties3` mediumint(8) unsigned NOT NULL,
- `properties4` mediumint(8) unsigned NOT NULL,
- `properties5` mediumint(8) unsigned NOT NULL,
- `reqSkill1` mediumint(8) unsigned NOT NULL,
- `reqSkill2` mediumint(8) unsigned NOT NULL,
- `reqSkill3` mediumint(8) unsigned NOT NULL,
- `reqSkill4` mediumint(8) unsigned NOT NULL,
- `reqSkill5` mediumint(8) unsigned NOT NULL,
- PRIMARY KEY (`id`)
-) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-/*!40101 SET character_set_client = @saved_cs_client */;
-
---
--- Table structure for table `aowow_loot_link`
---
-
-DROP TABLE IF EXISTS `aowow_loot_link`;
-/*!40101 SET @saved_cs_client = @@character_set_client */;
-/*!40101 SET character_set_client = utf8 */;
-CREATE TABLE `aowow_loot_link` (
- `npcId` mediumint(8) NOT NULL COMMENT 'id > 0 normal; id < 0 heroic',
- `objectId` mediumint(8) unsigned NOT NULL,
- `priority` tinyint(1) unsigned NOT NULL COMMENT '1: use this npc from group encounter (others 0)',
- `encounterId` mediumint(8) unsigned NOT NULL COMMENT 'as title reference',
- UNIQUE KEY `npcId` (`npcId`),
- KEY `objectId` (`objectId`)
-) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-/*!40101 SET character_set_client = @saved_cs_client */;
-
---
--- Table structure for table `aowow_mails`
---
-
-DROP TABLE IF EXISTS `aowow_mails`;
-/*!40101 SET @saved_cs_client = @@character_set_client */;
-/*!40101 SET character_set_client = utf8 */;
-CREATE TABLE `aowow_mails` (
- `id` smallint(5) NOT NULL,
- `subject_loc0` varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL,
- `subject_loc2` varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL,
- `subject_loc3` varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL,
- `subject_loc4` varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL,
- `subject_loc6` varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL,
- `subject_loc8` varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL,
- `text_loc0` text COLLATE utf8mb4_unicode_ci NOT NULL,
- `text_loc2` text COLLATE utf8mb4_unicode_ci NOT NULL,
- `text_loc3` text COLLATE utf8mb4_unicode_ci NOT NULL,
- `text_loc4` text COLLATE utf8mb4_unicode_ci NOT NULL,
- `text_loc6` text COLLATE utf8mb4_unicode_ci NOT NULL,
- `text_loc8` text COLLATE utf8mb4_unicode_ci NOT NULL,
- `attachment` smallint(5) unsigned NOT NULL DEFAULT 0,
- PRIMARY KEY (`id`)
-) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-/*!40101 SET character_set_client = @saved_cs_client */;
-
---
--- Table structure for table `aowow_objects`
---
-
-DROP TABLE IF EXISTS `aowow_objects`;
-/*!40101 SET @saved_cs_client = @@character_set_client */;
-/*!40101 SET character_set_client = utf8 */;
-CREATE TABLE `aowow_objects` (
- `id` mediumint(8) unsigned NOT NULL DEFAULT 0,
- `type` tinyint(3) unsigned NOT NULL DEFAULT 0,
- `typeCat` tinyint(3) NOT NULL DEFAULT 0,
- `event` smallint(5) unsigned NOT NULL DEFAULT 0,
- `displayId` mediumint(8) unsigned NOT NULL DEFAULT 0,
- `name_loc0` varchar(100) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
- `name_loc2` varchar(100) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
- `name_loc3` varchar(100) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
- `name_loc4` varchar(100) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
- `name_loc6` varchar(100) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
- `name_loc8` varchar(100) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
- `faction` smallint(5) unsigned NOT NULL DEFAULT 0,
- `flags` int(10) unsigned NOT NULL DEFAULT 0,
- `cuFlags` int(10) unsigned NOT NULL DEFAULT 0,
- `lootId` mediumint(8) unsigned NOT NULL DEFAULT 0,
- `lockId` smallint(5) unsigned NOT NULL DEFAULT 0,
- `reqSkill` smallint(5) unsigned NOT NULL DEFAULT 0,
- `pageTextId` smallint(5) unsigned NOT NULL DEFAULT 0,
- `linkedTrap` mediumint(8) unsigned NOT NULL DEFAULT 0,
- `reqQuest` smallint(5) unsigned NOT NULL DEFAULT 0,
- `spellFocusId` smallint(5) unsigned NOT NULL DEFAULT 0,
- `onUseSpell` mediumint(8) unsigned NOT NULL DEFAULT 0,
- `onSuccessSpell` mediumint(8) unsigned NOT NULL DEFAULT 0,
- `auraSpell` mediumint(8) unsigned NOT NULL DEFAULT 0,
- `triggeredSpell` mediumint(8) unsigned NOT NULL DEFAULT 0,
- `miscInfo` varchar(128) COLLATE utf8mb4_unicode_ci NOT NULL,
- `ScriptOrAI` varchar(64) COLLATE utf8mb4_unicode_ci NOT NULL,
- PRIMARY KEY (`id`),
- KEY `idx_name` (`name_loc0`)
-) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-/*!40101 SET character_set_client = @saved_cs_client */;
-
---
--- Table structure for table `aowow_pet`
---
-
-DROP TABLE IF EXISTS `aowow_pet`;
-/*!40101 SET @saved_cs_client = @@character_set_client */;
-/*!40101 SET character_set_client = utf8 */;
-CREATE TABLE `aowow_pet` (
- `id` int(11) NOT NULL,
- `category` mediumint(8) NOT NULL,
- `cuFlags` int(10) unsigned NOT NULL,
- `minLevel` smallint(6) NOT NULL,
- `maxLevel` smallint(6) NOT NULL,
- `foodMask` int(11) NOT NULL,
- `type` tinyint(4) NOT NULL,
- `exotic` tinyint(4) NOT NULL,
- `expansion` tinyint(4) NOT NULL,
- `name_loc0` varchar(64) COLLATE utf8mb4_unicode_ci NOT NULL,
- `name_loc2` varchar(64) COLLATE utf8mb4_unicode_ci NOT NULL,
- `name_loc3` varchar(64) COLLATE utf8mb4_unicode_ci NOT NULL,
- `name_loc4` varchar(64) COLLATE utf8mb4_unicode_ci NOT NULL,
- `name_loc6` varchar(64) COLLATE utf8mb4_unicode_ci NOT NULL,
- `name_loc8` varchar(64) COLLATE utf8mb4_unicode_ci NOT NULL,
- `iconId` smallint(5) unsigned NOT NULL DEFAULT 0,
- `skillLineId` mediumint(9) NOT NULL,
- `spellId1` mediumint(9) NOT NULL,
- `spellId2` mediumint(9) NOT NULL,
- `spellId3` mediumint(9) NOT NULL,
- `spellId4` mediumint(9) NOT NULL,
- `armor` mediumint(9) NOT NULL,
- `damage` mediumint(9) NOT NULL,
- `health` mediumint(9) NOT NULL,
- PRIMARY KEY (`id`),
- KEY `iconId` (`iconId`)
-) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-/*!40101 SET character_set_client = @saved_cs_client */;
-
---
--- Table structure for table `aowow_profiler_arena_team`
---
-
-DROP TABLE IF EXISTS `aowow_profiler_arena_team`;
-/*!40101 SET @saved_cs_client = @@character_set_client */;
-/*!40101 SET character_set_client = utf8 */;
-CREATE TABLE `aowow_profiler_arena_team` (
- `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
- `realm` tinyint(3) unsigned NOT NULL,
- `realmGUID` int(10) unsigned NOT NULL,
- `name` varchar(24) COLLATE utf8mb4_unicode_ci NOT NULL,
- `nameUrl` varchar(24) COLLATE utf8mb4_unicode_ci NOT NULL,
- `type` tinyint(3) unsigned NOT NULL DEFAULT 0,
- `cuFlags` int(11) unsigned NOT NULL,
- `rating` smallint(5) unsigned NOT NULL DEFAULT 0,
- `seasonGames` smallint(5) unsigned NOT NULL DEFAULT 0,
- `seasonWins` smallint(5) unsigned NOT NULL DEFAULT 0,
- `weekGames` smallint(5) unsigned NOT NULL DEFAULT 0,
- `weekWins` smallint(5) unsigned NOT NULL DEFAULT 0,
- `rank` int(10) unsigned NOT NULL DEFAULT 0,
- `backgroundColor` int(10) unsigned NOT NULL DEFAULT 0,
- `emblemStyle` tinyint(3) unsigned NOT NULL DEFAULT 0,
- `emblemColor` int(10) unsigned NOT NULL DEFAULT 0,
- `borderStyle` tinyint(3) unsigned NOT NULL DEFAULT 0,
- `borderColor` int(10) unsigned NOT NULL DEFAULT 0,
- PRIMARY KEY (`id`),
- UNIQUE KEY `realm_realmGUID` (`realm`,`realmGUID`),
- KEY `name` (`name`)
-) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-/*!40101 SET character_set_client = @saved_cs_client */;
-
---
--- Table structure for table `aowow_profiler_arena_team_member`
---
-
-DROP TABLE IF EXISTS `aowow_profiler_arena_team_member`;
-/*!40101 SET @saved_cs_client = @@character_set_client */;
-/*!40101 SET character_set_client = utf8 */;
-CREATE TABLE `aowow_profiler_arena_team_member` (
- `arenaTeamId` int(10) unsigned NOT NULL DEFAULT 0,
- `profileId` int(10) unsigned NOT NULL DEFAULT 0,
- `captain` tinyint(1) unsigned NOT NULL DEFAULT 0,
- `weekGames` smallint(5) unsigned NOT NULL DEFAULT 0,
- `weekWins` smallint(5) unsigned NOT NULL DEFAULT 0,
- `seasonGames` smallint(5) unsigned NOT NULL DEFAULT 0,
- `seasonWins` smallint(5) unsigned NOT NULL DEFAULT 0,
- `personalRating` smallint(5) unsigned NOT NULL DEFAULT 0,
- PRIMARY KEY (`arenaTeamId`,`profileId`),
- KEY `guid` (`profileId`),
- CONSTRAINT `FK_aowow_profiler_arena_team_member_aowow_profiler_arena_team` FOREIGN KEY (`arenaTeamId`) REFERENCES `aowow_profiler_arena_team` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
- CONSTRAINT `FK_aowow_profiler_arena_team_member_aowow_profiler_profiles` FOREIGN KEY (`profileId`) REFERENCES `aowow_profiler_profiles` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
-) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-/*!40101 SET character_set_client = @saved_cs_client */;
-
---
--- Table structure for table `aowow_profiler_completion`
---
-
-DROP TABLE IF EXISTS `aowow_profiler_completion`;
-/*!40101 SET @saved_cs_client = @@character_set_client */;
-/*!40101 SET character_set_client = utf8 */;
-CREATE TABLE `aowow_profiler_completion` (
- `id` int(11) unsigned NOT NULL,
- `type` smallint(6) unsigned NOT NULL,
- `typeId` mediumint(9) NOT NULL,
- `cur` int(11) DEFAULT NULL,
- `max` int(11) DEFAULT NULL,
- KEY `id` (`id`),
- KEY `type` (`type`),
- KEY `typeId` (`typeId`),
- CONSTRAINT `FK_pr_completion` FOREIGN KEY (`id`) REFERENCES `aowow_profiler_profiles` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
-) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-/*!40101 SET character_set_client = @saved_cs_client */;
-
---
--- Table structure for table `aowow_profiler_excludes`
---
-
-DROP TABLE IF EXISTS `aowow_profiler_excludes`;
-/*!40101 SET @saved_cs_client = @@character_set_client */;
-/*!40101 SET character_set_client = utf8 */;
-CREATE TABLE `aowow_profiler_excludes` (
- `type` smallint(5) unsigned NOT NULL,
- `typeId` mediumint(8) unsigned NOT NULL,
- `groups` smallint(5) unsigned NOT NULL COMMENT 'see exclude group defines',
- `comment` varchar(50) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT 'rebuilding profiler files will delete everything without a comment',
- PRIMARY KEY (`type`,`typeId`)
-) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-/*!40101 SET character_set_client = @saved_cs_client */;
-
---
--- Table structure for table `aowow_profiler_guild`
---
-
-DROP TABLE IF EXISTS `aowow_profiler_guild`;
-/*!40101 SET @saved_cs_client = @@character_set_client */;
-/*!40101 SET character_set_client = utf8 */;
-CREATE TABLE `aowow_profiler_guild` (
- `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
- `realm` int(10) unsigned NOT NULL,
- `realmGUID` int(10) unsigned NOT NULL,
- `cuFlags` int(10) unsigned NOT NULL DEFAULT 0,
- `name` varchar(26) COLLATE utf8mb4_unicode_ci NOT NULL,
- `nameUrl` varchar(26) COLLATE utf8mb4_unicode_ci NOT NULL,
- `emblemStyle` tinyint(3) unsigned NOT NULL DEFAULT 0,
- `emblemColor` tinyint(3) unsigned NOT NULL DEFAULT 0,
- `borderStyle` tinyint(3) unsigned NOT NULL DEFAULT 0,
- `borderColor` tinyint(3) unsigned NOT NULL DEFAULT 0,
- `backgroundColor` tinyint(3) unsigned NOT NULL DEFAULT 0,
- `info` varchar(500) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '',
- `createDate` int(10) unsigned NOT NULL DEFAULT 0,
- PRIMARY KEY (`id`),
- UNIQUE KEY `realm_realmGUID` (`realm`,`realmGUID`),
- KEY `name` (`name`)
-) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-/*!40101 SET character_set_client = @saved_cs_client */;
-
---
--- Table structure for table `aowow_profiler_guild_rank`
---
-
-DROP TABLE IF EXISTS `aowow_profiler_guild_rank`;
-/*!40101 SET @saved_cs_client = @@character_set_client */;
-/*!40101 SET character_set_client = utf8 */;
-CREATE TABLE `aowow_profiler_guild_rank` (
- `guildId` int(10) unsigned NOT NULL DEFAULT 0,
- `rank` tinyint(3) unsigned NOT NULL,
- `name` varchar(20) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '',
- PRIMARY KEY (`guildId`,`rank`),
- KEY `rank` (`rank`),
- CONSTRAINT `FK_aowow_profiler_guild_rank_aowow_profiler_guild` FOREIGN KEY (`guildId`) REFERENCES `aowow_profiler_guild` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
-) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-/*!40101 SET character_set_client = @saved_cs_client */;
-
---
--- Table structure for table `aowow_profiler_items`
---
-
-DROP TABLE IF EXISTS `aowow_profiler_items`;
-/*!40101 SET @saved_cs_client = @@character_set_client */;
-/*!40101 SET character_set_client = utf8 */;
-CREATE TABLE `aowow_profiler_items` (
- `id` int(11) unsigned DEFAULT NULL,
- `slot` tinyint(3) unsigned DEFAULT NULL,
- `item` mediumint(8) unsigned DEFAULT NULL,
- `subItem` smallint(6) DEFAULT NULL,
- `permEnchant` mediumint(8) unsigned DEFAULT NULL,
- `tempEnchant` mediumint(8) unsigned DEFAULT NULL,
- `extraSocket` tinyint(3) unsigned DEFAULT NULL COMMENT 'not used .. the appropriate gem slot is set to -1 instead',
- `gem1` mediumint(8) DEFAULT NULL,
- `gem2` mediumint(8) DEFAULT NULL,
- `gem3` mediumint(8) DEFAULT NULL,
- `gem4` mediumint(8) DEFAULT NULL,
- UNIQUE KEY `id_slot` (`id`,`slot`),
- KEY `id` (`id`),
- KEY `item` (`item`),
- CONSTRAINT `FK_pr_items` FOREIGN KEY (`id`) REFERENCES `aowow_profiler_profiles` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
-) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-/*!40101 SET character_set_client = @saved_cs_client */;
-
---
--- Table structure for table `aowow_profiler_pets`
---
-
-DROP TABLE IF EXISTS `aowow_profiler_pets`;
-/*!40101 SET @saved_cs_client = @@character_set_client */;
-/*!40101 SET character_set_client = utf8 */;
-CREATE TABLE `aowow_profiler_pets` (
- `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT,
- `owner` int(10) unsigned DEFAULT NULL,
- `name` varchar(50) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
- `family` tinyint(3) unsigned DEFAULT NULL,
- `npc` smallint(5) unsigned DEFAULT NULL,
- `displayId` smallint(5) unsigned DEFAULT NULL,
- `talents` varchar(20) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
- PRIMARY KEY (`id`),
- KEY `owner` (`owner`),
- CONSTRAINT `FK_pr_pets` FOREIGN KEY (`owner`) REFERENCES `aowow_profiler_profiles` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
-) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-/*!40101 SET character_set_client = @saved_cs_client */;
-
---
--- Table structure for table `aowow_profiler_profiles`
---
-
-DROP TABLE IF EXISTS `aowow_profiler_profiles`;
-/*!40101 SET @saved_cs_client = @@character_set_client */;
-/*!40101 SET character_set_client = utf8 */;
-CREATE TABLE `aowow_profiler_profiles` (
- `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
- `realm` tinyint(3) unsigned DEFAULT NULL,
- `realmGUID` int(11) unsigned DEFAULT NULL,
- `cuFlags` int(11) unsigned NOT NULL DEFAULT 0,
- `sourceId` int(11) unsigned DEFAULT NULL,
- `sourceName` varchar(50) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
- `copy` int(10) unsigned DEFAULT NULL,
- `icon` varchar(50) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
- `user` int(11) unsigned DEFAULT NULL,
- `name` varchar(50) COLLATE utf8mb4_bin NOT NULL,
- `renameItr` tinyint(3) unsigned NOT NULL,
- `race` tinyint(3) unsigned NOT NULL,
- `class` tinyint(3) unsigned NOT NULL,
- `level` tinyint(3) unsigned NOT NULL,
- `gender` tinyint(3) unsigned NOT NULL,
- `guild` int(10) unsigned DEFAULT NULL,
- `guildrank` tinyint(3) unsigned DEFAULT NULL COMMENT '0: guild master',
- `skincolor` tinyint(3) unsigned NOT NULL,
- `hairstyle` tinyint(3) unsigned NOT NULL,
- `haircolor` tinyint(3) unsigned NOT NULL,
- `facetype` tinyint(3) unsigned NOT NULL,
- `features` tinyint(3) unsigned NOT NULL,
- `nomodelMask` int(11) unsigned NOT NULL DEFAULT 0,
- `title` tinyint(3) unsigned NOT NULL,
- `description` mediumtext COLLATE utf8mb4_unicode_ci DEFAULT NULL,
- `playedtime` int(11) unsigned NOT NULL,
- `gearscore` smallint(5) unsigned NOT NULL,
- `achievementpoints` smallint(5) unsigned NOT NULL,
- `lastupdated` int(11) NOT NULL,
- `talenttree1` tinyint(4) unsigned NOT NULL COMMENT 'points spend in 1st tree',
- `talenttree2` tinyint(4) unsigned NOT NULL COMMENT 'points spend in 2nd tree',
- `talenttree3` tinyint(4) unsigned NOT NULL COMMENT 'points spend in 3rd tree',
- `talentbuild1` varchar(105) COLLATE utf8mb4_unicode_ci NOT NULL,
- `talentbuild2` varchar(105) COLLATE utf8mb4_unicode_ci NOT NULL,
- `glyphs1` varchar(45) COLLATE utf8mb4_unicode_ci NOT NULL,
- `glyphs2` varchar(45) COLLATE utf8mb4_unicode_ci NOT NULL,
- `activespec` tinyint(1) unsigned NOT NULL,
- PRIMARY KEY (`id`),
- UNIQUE KEY `realm_realmGUID` (`realm`,`realmGUID`),
- KEY `user` (`user`),
- KEY `guild` (`guild`),
- KEY `name` (`name`),
- CONSTRAINT `FK_aowow_profiler_profiles_aowow_profiler_guild` FOREIGN KEY (`guild`) REFERENCES `aowow_profiler_guild` (`id`) ON DELETE SET NULL ON UPDATE CASCADE
-) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-/*!40101 SET character_set_client = @saved_cs_client */;
-
---
--- Table structure for table `aowow_profiler_sync`
---
-
-DROP TABLE IF EXISTS `aowow_profiler_sync`;
-/*!40101 SET @saved_cs_client = @@character_set_client */;
-/*!40101 SET character_set_client = utf8 */;
-CREATE TABLE `aowow_profiler_sync` (
- `realm` tinyint(3) unsigned NOT NULL,
- `realmGUID` int(10) unsigned NOT NULL,
- `type` smallint(5) unsigned NOT NULL,
- `typeId` int(10) unsigned NOT NULL,
- `requestTime` int(10) unsigned NOT NULL,
- `status` tinyint(3) unsigned NOT NULL,
- `errorCode` tinyint(3) unsigned NOT NULL DEFAULT 0,
- UNIQUE KEY `realm_realmGUID_type_typeId` (`realm`,`realmGUID`,`type`),
- UNIQUE KEY `type_typeId` (`type`,`typeId`)
-) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-/*!40101 SET character_set_client = @saved_cs_client */;
-
---
--- Table structure for table `aowow_quests`
---
-
-DROP TABLE IF EXISTS `aowow_quests`;
-/*!40101 SET @saved_cs_client = @@character_set_client */;
-/*!40101 SET character_set_client = utf8 */;
-CREATE TABLE `aowow_quests` (
- `id` mediumint(8) unsigned NOT NULL DEFAULT 0,
- `method` tinyint(3) unsigned NOT NULL DEFAULT 2,
- `level` smallint(3) NOT NULL DEFAULT 1,
- `minLevel` tinyint(3) unsigned NOT NULL DEFAULT 0,
- `maxLevel` tinyint(3) unsigned NOT NULL DEFAULT 0,
- `zoneOrSort` smallint(6) NOT NULL DEFAULT 0,
- `zoneOrSortBak` smallint(6) NOT NULL DEFAULT 0,
- `type` smallint(5) unsigned NOT NULL DEFAULT 0,
- `suggestedPlayers` tinyint(3) unsigned NOT NULL DEFAULT 0,
- `timeLimit` int(10) unsigned NOT NULL DEFAULT 0,
- `eventId` smallint(5) unsigned NOT NULL DEFAULT 0,
- `prevQuestId` mediumint(8) NOT NULL DEFAULT 0,
- `nextQuestId` mediumint(8) NOT NULL DEFAULT 0,
- `breadcrumbForQuestId` mediumint(8) NOT NULL DEFAULT 0,
- `exclusiveGroup` mediumint(8) NOT NULL DEFAULT 0,
- `nextQuestIdChain` mediumint(8) unsigned NOT NULL DEFAULT 0,
- `flags` int(10) unsigned NOT NULL DEFAULT 0,
- `specialFlags` tinyint(3) unsigned NOT NULL DEFAULT 0,
- `cuFlags` int(10) unsigned NOT NULL DEFAULT 0,
- `reqClassMask` smallint(5) unsigned NOT NULL DEFAULT 0,
- `reqRaceMask` smallint(5) unsigned NOT NULL DEFAULT 0,
- `reqSkillId` smallint(5) unsigned NOT NULL DEFAULT 0,
- `reqSkillPoints` smallint(5) unsigned NOT NULL DEFAULT 0,
- `reqFactionId1` smallint(5) unsigned NOT NULL DEFAULT 0,
- `reqFactionId2` smallint(5) unsigned NOT NULL DEFAULT 0,
- `reqFactionValue1` mediumint(8) NOT NULL DEFAULT 0,
- `reqFactionValue2` mediumint(8) NOT NULL DEFAULT 0,
- `reqMinRepFaction` smallint(5) unsigned NOT NULL DEFAULT 0,
- `reqMaxRepFaction` smallint(5) unsigned NOT NULL DEFAULT 0,
- `reqMinRepValue` mediumint(8) NOT NULL DEFAULT 0,
- `reqMaxRepValue` mediumint(8) NOT NULL DEFAULT 0,
- `reqPlayerKills` tinyint(3) unsigned NOT NULL DEFAULT 0,
- `sourceItemId` mediumint(8) unsigned NOT NULL DEFAULT 0,
- `sourceItemCount` tinyint(3) unsigned NOT NULL DEFAULT 0,
- `sourceSpellId` mediumint(8) unsigned NOT NULL DEFAULT 0,
- `rewardXP` mediumint(8) unsigned NOT NULL DEFAULT 0,
- `rewardOrReqMoney` int(11) NOT NULL DEFAULT 0,
- `rewardMoneyMaxLevel` int(10) unsigned NOT NULL DEFAULT 0,
- `rewardSpell` mediumint(8) unsigned NOT NULL DEFAULT 0,
- `rewardSpellCast` int(11) NOT NULL DEFAULT 0,
- `rewardHonorPoints` int(11) NOT NULL DEFAULT 0,
- `rewardMailTemplateId` mediumint(8) unsigned NOT NULL DEFAULT 0,
- `rewardMailDelay` int(11) unsigned NOT NULL DEFAULT 0,
- `rewardTitleId` tinyint(3) unsigned NOT NULL DEFAULT 0,
- `rewardTalents` tinyint(3) unsigned NOT NULL DEFAULT 0,
- `rewardArenaPoints` smallint(5) unsigned NOT NULL DEFAULT 0,
- `rewardItemId1` mediumint(8) unsigned NOT NULL DEFAULT 0,
- `rewardItemId2` mediumint(8) unsigned NOT NULL DEFAULT 0,
- `rewardItemId3` mediumint(8) unsigned NOT NULL DEFAULT 0,
- `rewardItemId4` mediumint(8) unsigned NOT NULL DEFAULT 0,
- `rewardItemCount1` smallint(5) unsigned NOT NULL DEFAULT 0,
- `rewardItemCount2` smallint(5) unsigned NOT NULL DEFAULT 0,
- `rewardItemCount3` smallint(5) unsigned NOT NULL DEFAULT 0,
- `rewardItemCount4` smallint(5) unsigned NOT NULL DEFAULT 0,
- `rewardChoiceItemId1` mediumint(8) unsigned NOT NULL DEFAULT 0,
- `rewardChoiceItemId2` mediumint(8) unsigned NOT NULL DEFAULT 0,
- `rewardChoiceItemId3` mediumint(8) unsigned NOT NULL DEFAULT 0,
- `rewardChoiceItemId4` mediumint(8) unsigned NOT NULL DEFAULT 0,
- `rewardChoiceItemId5` mediumint(8) unsigned NOT NULL DEFAULT 0,
- `rewardChoiceItemId6` mediumint(8) unsigned NOT NULL DEFAULT 0,
- `rewardChoiceItemCount1` smallint(5) unsigned NOT NULL DEFAULT 0,
- `rewardChoiceItemCount2` smallint(5) unsigned NOT NULL DEFAULT 0,
- `rewardChoiceItemCount3` smallint(5) unsigned NOT NULL DEFAULT 0,
- `rewardChoiceItemCount4` smallint(5) unsigned NOT NULL DEFAULT 0,
- `rewardChoiceItemCount5` smallint(5) unsigned NOT NULL DEFAULT 0,
- `rewardChoiceItemCount6` smallint(5) unsigned NOT NULL DEFAULT 0,
- `rewardFactionId1` smallint(5) unsigned NOT NULL DEFAULT 0 COMMENT 'faction id from Faction.dbc in this case',
- `rewardFactionId2` smallint(5) unsigned NOT NULL DEFAULT 0 COMMENT 'faction id from Faction.dbc in this case',
- `rewardFactionId3` smallint(5) unsigned NOT NULL DEFAULT 0 COMMENT 'faction id from Faction.dbc in this case',
- `rewardFactionId4` smallint(5) unsigned NOT NULL DEFAULT 0 COMMENT 'faction id from Faction.dbc in this case',
- `rewardFactionId5` smallint(5) unsigned NOT NULL DEFAULT 0 COMMENT 'faction id from Faction.dbc in this case',
- `rewardFactionValue1` mediumint(8) NOT NULL DEFAULT 0,
- `rewardFactionValue2` mediumint(8) NOT NULL DEFAULT 0,
- `rewardFactionValue3` mediumint(8) NOT NULL DEFAULT 0,
- `rewardFactionValue4` mediumint(8) NOT NULL DEFAULT 0,
- `rewardFactionValue5` mediumint(8) NOT NULL DEFAULT 0,
- `name_loc0` mediumtext COLLATE utf8mb4_unicode_ci DEFAULT NULL,
- `name_loc2` mediumtext COLLATE utf8mb4_unicode_ci DEFAULT NULL,
- `name_loc3` mediumtext COLLATE utf8mb4_unicode_ci DEFAULT NULL,
- `name_loc4` mediumtext COLLATE utf8mb4_unicode_ci DEFAULT NULL,
- `name_loc6` mediumtext COLLATE utf8mb4_unicode_ci DEFAULT NULL,
- `name_loc8` mediumtext COLLATE utf8mb4_unicode_ci DEFAULT NULL,
- `objectives_loc0` mediumtext COLLATE utf8mb4_unicode_ci DEFAULT NULL,
- `objectives_loc2` mediumtext COLLATE utf8mb4_unicode_ci DEFAULT NULL,
- `objectives_loc3` mediumtext COLLATE utf8mb4_unicode_ci DEFAULT NULL,
- `objectives_loc4` mediumtext COLLATE utf8mb4_unicode_ci DEFAULT NULL,
- `objectives_loc6` mediumtext COLLATE utf8mb4_unicode_ci DEFAULT NULL,
- `objectives_loc8` mediumtext COLLATE utf8mb4_unicode_ci DEFAULT NULL,
- `details_loc0` mediumtext COLLATE utf8mb4_unicode_ci DEFAULT NULL,
- `details_loc2` mediumtext COLLATE utf8mb4_unicode_ci DEFAULT NULL,
- `details_loc3` mediumtext COLLATE utf8mb4_unicode_ci DEFAULT NULL,
- `details_loc4` mediumtext COLLATE utf8mb4_unicode_ci DEFAULT NULL,
- `details_loc6` mediumtext COLLATE utf8mb4_unicode_ci DEFAULT NULL,
- `details_loc8` mediumtext COLLATE utf8mb4_unicode_ci DEFAULT NULL,
- `end_loc0` mediumtext COLLATE utf8mb4_unicode_ci DEFAULT NULL,
- `end_loc2` mediumtext COLLATE utf8mb4_unicode_ci DEFAULT NULL,
- `end_loc3` mediumtext COLLATE utf8mb4_unicode_ci DEFAULT NULL,
- `end_loc4` mediumtext COLLATE utf8mb4_unicode_ci DEFAULT NULL,
- `end_loc6` mediumtext COLLATE utf8mb4_unicode_ci DEFAULT NULL,
- `end_loc8` mediumtext COLLATE utf8mb4_unicode_ci DEFAULT NULL,
- `offerReward_loc0` mediumtext COLLATE utf8mb4_unicode_ci DEFAULT NULL,
- `offerReward_loc2` mediumtext COLLATE utf8mb4_unicode_ci DEFAULT NULL,
- `offerReward_loc3` mediumtext COLLATE utf8mb4_unicode_ci DEFAULT NULL,
- `offerReward_loc4` mediumtext COLLATE utf8mb4_unicode_ci DEFAULT NULL,
- `offerReward_loc6` mediumtext COLLATE utf8mb4_unicode_ci DEFAULT NULL,
- `offerReward_loc8` mediumtext COLLATE utf8mb4_unicode_ci DEFAULT NULL,
- `requestItems_loc0` mediumtext COLLATE utf8mb4_unicode_ci DEFAULT NULL,
- `requestItems_loc2` mediumtext COLLATE utf8mb4_unicode_ci DEFAULT NULL,
- `requestItems_loc3` mediumtext COLLATE utf8mb4_unicode_ci DEFAULT NULL,
- `requestItems_loc4` mediumtext COLLATE utf8mb4_unicode_ci DEFAULT NULL,
- `requestItems_loc6` mediumtext COLLATE utf8mb4_unicode_ci DEFAULT NULL,
- `requestItems_loc8` mediumtext COLLATE utf8mb4_unicode_ci DEFAULT NULL,
- `completed_loc0` mediumtext COLLATE utf8mb4_unicode_ci DEFAULT NULL,
- `completed_loc2` mediumtext COLLATE utf8mb4_unicode_ci DEFAULT NULL,
- `completed_loc3` mediumtext COLLATE utf8mb4_unicode_ci DEFAULT NULL,
- `completed_loc4` mediumtext COLLATE utf8mb4_unicode_ci DEFAULT NULL,
- `completed_loc6` mediumtext COLLATE utf8mb4_unicode_ci DEFAULT NULL,
- `completed_loc8` mediumtext COLLATE utf8mb4_unicode_ci DEFAULT NULL,
- `reqNpcOrGo1` mediumint(8) NOT NULL DEFAULT 0,
- `reqNpcOrGo2` mediumint(8) NOT NULL DEFAULT 0,
- `reqNpcOrGo3` mediumint(8) NOT NULL DEFAULT 0,
- `reqNpcOrGo4` mediumint(8) NOT NULL DEFAULT 0,
- `reqNpcOrGoCount1` smallint(5) unsigned NOT NULL DEFAULT 0,
- `reqNpcOrGoCount2` smallint(5) unsigned NOT NULL DEFAULT 0,
- `reqNpcOrGoCount3` smallint(5) unsigned NOT NULL DEFAULT 0,
- `reqNpcOrGoCount4` smallint(5) unsigned NOT NULL DEFAULT 0,
- `reqSourceItemId1` mediumint(8) unsigned NOT NULL DEFAULT 0,
- `reqSourceItemId2` mediumint(8) unsigned NOT NULL DEFAULT 0,
- `reqSourceItemId3` mediumint(8) unsigned NOT NULL DEFAULT 0,
- `reqSourceItemId4` mediumint(8) unsigned NOT NULL DEFAULT 0,
- `reqSourceItemCount1` smallint(5) unsigned NOT NULL DEFAULT 0,
- `reqSourceItemCount2` smallint(5) unsigned NOT NULL DEFAULT 0,
- `reqSourceItemCount3` smallint(5) unsigned NOT NULL DEFAULT 0,
- `reqSourceItemCount4` smallint(5) unsigned NOT NULL DEFAULT 0,
- `reqItemId1` mediumint(8) unsigned NOT NULL DEFAULT 0,
- `reqItemId2` mediumint(8) unsigned NOT NULL DEFAULT 0,
- `reqItemId3` mediumint(8) unsigned NOT NULL DEFAULT 0,
- `reqItemId4` mediumint(8) unsigned NOT NULL DEFAULT 0,
- `reqItemId5` mediumint(8) unsigned NOT NULL DEFAULT 0,
- `reqItemId6` mediumint(8) unsigned NOT NULL DEFAULT 0,
- `reqItemCount1` smallint(5) unsigned NOT NULL DEFAULT 0,
- `reqItemCount2` smallint(5) unsigned NOT NULL DEFAULT 0,
- `reqItemCount3` smallint(5) unsigned NOT NULL DEFAULT 0,
- `reqItemCount4` smallint(5) unsigned NOT NULL DEFAULT 0,
- `reqItemCount5` smallint(5) unsigned NOT NULL DEFAULT 0,
- `reqItemCount6` smallint(5) unsigned NOT NULL DEFAULT 0,
- `objectiveText1_loc0` mediumtext COLLATE utf8mb4_unicode_ci DEFAULT NULL,
- `objectiveText1_loc2` mediumtext COLLATE utf8mb4_unicode_ci DEFAULT NULL,
- `objectiveText1_loc3` mediumtext COLLATE utf8mb4_unicode_ci DEFAULT NULL,
- `objectiveText1_loc4` mediumtext COLLATE utf8mb4_unicode_ci DEFAULT NULL,
- `objectiveText1_loc6` mediumtext COLLATE utf8mb4_unicode_ci DEFAULT NULL,
- `objectiveText1_loc8` mediumtext COLLATE utf8mb4_unicode_ci DEFAULT NULL,
- `objectiveText2_loc0` mediumtext COLLATE utf8mb4_unicode_ci DEFAULT NULL,
- `objectiveText2_loc2` mediumtext COLLATE utf8mb4_unicode_ci DEFAULT NULL,
- `objectiveText2_loc3` mediumtext COLLATE utf8mb4_unicode_ci DEFAULT NULL,
- `objectiveText2_loc4` mediumtext COLLATE utf8mb4_unicode_ci DEFAULT NULL,
- `objectiveText2_loc6` mediumtext COLLATE utf8mb4_unicode_ci DEFAULT NULL,
- `objectiveText2_loc8` mediumtext COLLATE utf8mb4_unicode_ci DEFAULT NULL,
- `objectiveText3_loc0` mediumtext COLLATE utf8mb4_unicode_ci DEFAULT NULL,
- `objectiveText3_loc2` mediumtext COLLATE utf8mb4_unicode_ci DEFAULT NULL,
- `objectiveText3_loc3` mediumtext COLLATE utf8mb4_unicode_ci DEFAULT NULL,
- `objectiveText3_loc4` mediumtext COLLATE utf8mb4_unicode_ci DEFAULT NULL,
- `objectiveText3_loc6` mediumtext COLLATE utf8mb4_unicode_ci DEFAULT NULL,
- `objectiveText3_loc8` mediumtext COLLATE utf8mb4_unicode_ci DEFAULT NULL,
- `objectiveText4_loc0` mediumtext COLLATE utf8mb4_unicode_ci DEFAULT NULL,
- `objectiveText4_loc2` mediumtext COLLATE utf8mb4_unicode_ci DEFAULT NULL,
- `objectiveText4_loc3` mediumtext COLLATE utf8mb4_unicode_ci DEFAULT NULL,
- `objectiveText4_loc4` mediumtext COLLATE utf8mb4_unicode_ci DEFAULT NULL,
- `objectiveText4_loc6` mediumtext COLLATE utf8mb4_unicode_ci DEFAULT NULL,
- `objectiveText4_loc8` mediumtext COLLATE utf8mb4_unicode_ci DEFAULT NULL,
- PRIMARY KEY (`id`),
- KEY `nextQuestIdChain` (`nextQuestIdChain`)
-) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-/*!40101 SET character_set_client = @saved_cs_client */;
-
---
--- Table structure for table `aowow_quests_startend`
---
-
-DROP TABLE IF EXISTS `aowow_quests_startend`;
-/*!40101 SET @saved_cs_client = @@character_set_client */;
-/*!40101 SET character_set_client = utf8 */;
-CREATE TABLE `aowow_quests_startend` (
- `type` tinyint(4) unsigned NOT NULL,
- `typeId` mediumint(9) unsigned NOT NULL,
- `questId` mediumint(9) unsigned NOT NULL,
- `method` tinyint(4) unsigned NOT NULL COMMENT '&0x1: starts; &0x2:ends',
- `eventId` smallint(6) unsigned NOT NULL DEFAULT 0,
- PRIMARY KEY (`type`,`typeId`,`questId`)
-) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-/*!40101 SET character_set_client = @saved_cs_client */;
-
---
--- Table structure for table `aowow_races`
---
-
-DROP TABLE IF EXISTS `aowow_races`;
-/*!40101 SET @saved_cs_client = @@character_set_client */;
-/*!40101 SET character_set_client = utf8 */;
-CREATE TABLE `aowow_races` (
- `id` int(16) NOT NULL,
- `classMask` bigint(20) NOT NULL,
- `flags` bigint(20) NOT NULL,
- `cuFlags` int(10) unsigned NOT NULL,
- `factionId` bigint(20) NOT NULL,
- `startAreaId` bigint(20) NOT NULL,
- `leader` bigint(20) NOT NULL,
- `baseLanguage` bigint(20) NOT NULL,
- `side` int(3) NOT NULL,
- `fileString` varchar(64) COLLATE utf8mb4_unicode_ci NOT NULL,
- `name_loc0` varchar(64) COLLATE utf8mb4_unicode_ci NOT NULL,
- `name_loc2` varchar(64) COLLATE utf8mb4_unicode_ci NOT NULL,
- `name_loc3` varchar(64) COLLATE utf8mb4_unicode_ci NOT NULL,
- `name_loc4` varchar(64) COLLATE utf8mb4_unicode_ci NOT NULL,
- `name_loc6` varchar(64) COLLATE utf8mb4_unicode_ci NOT NULL,
- `name_loc8` varchar(64) COLLATE utf8mb4_unicode_ci NOT NULL,
- `expansion` int(1) NOT NULL,
- PRIMARY KEY (`id`)
-) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-/*!40101 SET character_set_client = @saved_cs_client */;
-
---
--- Table structure for table `aowow_races_sounds`
---
-
-DROP TABLE IF EXISTS `aowow_races_sounds`;
-/*!40101 SET @saved_cs_client = @@character_set_client */;
-/*!40101 SET character_set_client = utf8 */;
-CREATE TABLE `aowow_races_sounds` (
- `raceId` tinyint(3) unsigned NOT NULL,
- `soundId` smallint(5) unsigned NOT NULL,
- `gender` tinyint(1) unsigned NOT NULL,
- UNIQUE KEY `race_soundId_gender` (`raceId`,`soundId`,`gender`),
- KEY `race` (`raceId`),
- KEY `soundId` (`soundId`)
-) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-/*!40101 SET character_set_client = @saved_cs_client */;
-
---
--- Table structure for table `aowow_reports`
---
-
-DROP TABLE IF EXISTS `aowow_reports`;
-/*!40101 SET @saved_cs_client = @@character_set_client */;
-/*!40101 SET character_set_client = utf8 */;
-CREATE TABLE `aowow_reports` (
- `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT,
- `userId` mediumint(8) unsigned NOT NULL,
- `assigned` mediumint(8) unsigned NOT NULL DEFAULT 0,
- `status` tinyint(3) unsigned NOT NULL DEFAULT 0 COMMENT '0:new; 1:solved; 2:rejected',
- `createDate` int(10) unsigned NOT NULL,
- `mode` tinyint(3) unsigned NOT NULL,
- `reason` tinyint(3) unsigned NOT NULL,
- `subject` mediumint(9) NOT NULL DEFAULT 0,
- `ip` varchar(50) COLLATE utf8mb4_unicode_ci NOT NULL,
- `description` mediumtext COLLATE utf8mb4_unicode_ci NOT NULL,
- `userAgent` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
- `appName` varchar(32) COLLATE utf8mb4_unicode_ci NOT NULL,
- `url` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
- `relatedUrl` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
- `email` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
- PRIMARY KEY (`id`),
- KEY `userId` (`userId`)
-) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-/*!40101 SET character_set_client = @saved_cs_client */;
-
---
--- Table structure for table `aowow_scalingstatdistribution`
---
-
-DROP TABLE IF EXISTS `aowow_scalingstatdistribution`;
-/*!40101 SET @saved_cs_client = @@character_set_client */;
-/*!40101 SET character_set_client = utf8 */;
-CREATE TABLE `aowow_scalingstatdistribution` (
- `id` smallint(5) unsigned NOT NULL,
- `statMod1` tinyint(4) NOT NULL,
- `statMod2` tinyint(4) NOT NULL,
- `statMod3` tinyint(4) NOT NULL,
- `statMod4` tinyint(4) NOT NULL,
- `statMod5` tinyint(4) NOT NULL,
- `statMod6` tinyint(4) NOT NULL,
- `statMod7` tinyint(4) NOT NULL,
- `statMod8` tinyint(4) NOT NULL,
- `statMod9` tinyint(4) NOT NULL,
- `statMod10` tinyint(4) NOT NULL,
- `modifier1` smallint(5) unsigned NOT NULL,
- `modifier2` smallint(5) unsigned NOT NULL,
- `modifier3` smallint(5) unsigned NOT NULL,
- `modifier4` smallint(5) unsigned NOT NULL,
- `modifier5` smallint(5) unsigned NOT NULL,
- `modifier6` smallint(5) unsigned NOT NULL,
- `modifier7` smallint(5) unsigned NOT NULL,
- `modifier8` smallint(5) unsigned NOT NULL,
- `modifier9` smallint(5) unsigned NOT NULL,
- `modifier10` smallint(5) unsigned NOT NULL,
- `maxLevel` tinyint(3) unsigned NOT NULL,
- PRIMARY KEY (`id`)
-) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-/*!40101 SET character_set_client = @saved_cs_client */;
-
---
--- Table structure for table `aowow_scalingstatvalues`
---
-
-DROP TABLE IF EXISTS `aowow_scalingstatvalues`;
-/*!40101 SET @saved_cs_client = @@character_set_client */;
-/*!40101 SET character_set_client = utf8 */;
-CREATE TABLE `aowow_scalingstatvalues` (
- `id` tinyint(3) unsigned NOT NULL,
- `shoulderMultiplier` tinyint(3) unsigned NOT NULL,
- `trinketMultiplier` tinyint(3) unsigned NOT NULL,
- `weaponMultiplier` tinyint(3) unsigned NOT NULL,
- `rangedMultiplier` tinyint(3) unsigned NOT NULL,
- `clothShoulderArmor` tinyint(3) unsigned NOT NULL,
- `leatherShoulderArmor` smallint(5) unsigned NOT NULL,
- `mailShoulderArmor` smallint(5) unsigned NOT NULL,
- `plateShoulderArmor` smallint(5) unsigned NOT NULL,
- `weaponDPS1H` tinyint(3) unsigned NOT NULL,
- `weaponDPS2H` tinyint(3) unsigned NOT NULL,
- `casterDPS1H` tinyint(3) unsigned NOT NULL,
- `casterDPS2H` tinyint(3) unsigned NOT NULL,
- `rangedDPS` tinyint(3) unsigned NOT NULL,
- `wandDPS` tinyint(3) unsigned NOT NULL,
- `spellPower` smallint(5) unsigned NOT NULL,
- `primBudged` tinyint(3) unsigned NOT NULL,
- `tertBudged` tinyint(3) unsigned NOT NULL,
- `clothCloakArmor` tinyint(3) unsigned NOT NULL,
- `clothChestArmor` smallint(5) unsigned NOT NULL,
- `leatherChestArmor` smallint(5) unsigned NOT NULL,
- `mailChestArmor` smallint(5) unsigned NOT NULL,
- `plateChestArmor` smallint(5) unsigned NOT NULL,
- PRIMARY KEY (`id`)
-) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-/*!40101 SET character_set_client = @saved_cs_client */;
-
---
--- Table structure for table `aowow_screenshots`
---
-
-DROP TABLE IF EXISTS `aowow_screenshots`;
-/*!40101 SET @saved_cs_client = @@character_set_client */;
-/*!40101 SET character_set_client = utf8 */;
-CREATE TABLE `aowow_screenshots` (
- `id` int(16) unsigned NOT NULL AUTO_INCREMENT,
- `type` smallint(5) unsigned NOT NULL,
- `typeId` mediumint(9) NOT NULL,
- `userIdOwner` int(10) unsigned DEFAULT NULL,
- `date` int(32) unsigned NOT NULL,
- `width` smallint(5) unsigned NOT NULL,
- `height` smallint(5) unsigned NOT NULL,
- `caption` varchar(250) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
- `status` tinyint(3) unsigned NOT NULL COMMENT 'see defines.php - CC_FLAG_*',
- `userIdApprove` int(10) unsigned DEFAULT NULL,
- `userIdDelete` int(10) unsigned DEFAULT NULL,
- PRIMARY KEY (`id`),
- KEY `type` (`type`,`typeId`),
- KEY `FK_acc_ss` (`userIdOwner`),
- CONSTRAINT `FK_acc_ss` FOREIGN KEY (`userIdOwner`) REFERENCES `aowow_account` (`id`) ON DELETE SET NULL ON UPDATE CASCADE
-) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=COMPACT;
-/*!40101 SET character_set_client = @saved_cs_client */;
-
---
--- Table structure for table `aowow_setup_custom_data`
---
-
-DROP TABLE IF EXISTS `aowow_setup_custom_data`;
-/*!40101 SET @saved_cs_client = @@character_set_client */;
-/*!50503 SET character_set_client = utf8mb4 */;
-CREATE TABLE `aowow_setup_custom_data` (
- `command` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '',
- `entry` int NOT NULL DEFAULT '0' COMMENT 'typeId',
- `field` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '',
- `value` varchar(100) COLLATE utf8mb4_general_ci DEFAULT NULL,
- `comment` text COLLATE utf8mb4_general_ci,
- KEY `aowow_setup_custom_data_command_IDX` (`command`) USING BTREE
-) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
-/*!40101 SET character_set_client = @saved_cs_client */;
-
---
--- Table structure for table `aowow_shapeshiftforms`
---
-
-DROP TABLE IF EXISTS `aowow_shapeshiftforms`;
-/*!40101 SET @saved_cs_client = @@character_set_client */;
-/*!40101 SET character_set_client = utf8 */;
-CREATE TABLE `aowow_shapeshiftforms` (
- `Id` bigint(20) NOT NULL,
- `flags` bigint(20) NOT NULL,
- `creatureType` bigint(20) NOT NULL,
- `displayIdA` bigint(20) NOT NULL,
- `displayIdH` bigint(20) NOT NULL,
- `spellId1` bigint(20) NOT NULL,
- `spellId2` bigint(20) NOT NULL,
- `spellId3` bigint(20) NOT NULL,
- `spellId4` bigint(20) NOT NULL,
- `spellId5` bigint(20) NOT NULL,
- `spellId6` bigint(20) NOT NULL,
- `spellId7` bigint(20) NOT NULL,
- `spellId8` bigint(20) NOT NULL,
- `comment` varchar(30) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
- PRIMARY KEY (`Id`)
-) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-/*!40101 SET character_set_client = @saved_cs_client */;
-
---
--- Table structure for table `aowow_skillline`
---
-
-DROP TABLE IF EXISTS `aowow_skillline`;
-/*!40101 SET @saved_cs_client = @@character_set_client */;
-/*!40101 SET character_set_client = utf8 */;
-CREATE TABLE `aowow_skillline` (
- `Id` smallint(5) unsigned NOT NULL,
- `typeCat` tinyint(4) NOT NULL,
- `cuFlags` int(10) unsigned NOT NULL,
- `categoryId` tinyint(3) unsigned NOT NULL,
- `name_loc0` varchar(64) COLLATE utf8mb4_unicode_ci NOT NULL,
- `name_loc2` varchar(64) COLLATE utf8mb4_unicode_ci NOT NULL,
- `name_loc3` varchar(64) COLLATE utf8mb4_unicode_ci NOT NULL,
- `name_loc4` varchar(64) COLLATE utf8mb4_unicode_ci NOT NULL,
- `name_loc6` varchar(64) COLLATE utf8mb4_unicode_ci NOT NULL,
- `name_loc8` varchar(64) COLLATE utf8mb4_unicode_ci NOT NULL,
- `description_loc0` mediumtext COLLATE utf8mb4_unicode_ci NOT NULL,
- `description_loc2` mediumtext COLLATE utf8mb4_unicode_ci NOT NULL,
- `description_loc3` mediumtext COLLATE utf8mb4_unicode_ci NOT NULL,
- `description_loc4` mediumtext COLLATE utf8mb4_unicode_ci NOT NULL,
- `description_loc6` mediumtext COLLATE utf8mb4_unicode_ci NOT NULL,
- `description_loc8` mediumtext COLLATE utf8mb4_unicode_ci NOT NULL,
- `iconId` smallint(5) unsigned NOT NULL DEFAULT 0,
- `iconIdBak` smallint(5) unsigned NOT NULL DEFAULT 0,
- `professionMask` smallint(5) unsigned NOT NULL,
- `recipeSubClass` tinyint(3) unsigned NOT NULL,
- `specializations` varchar(30) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT 'space-separated spellIds',
- PRIMARY KEY (`Id`)
-) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-/*!40101 SET character_set_client = @saved_cs_client */;
-
---
--- Table structure for table `aowow_sounds`
---
-
-DROP TABLE IF EXISTS `aowow_sounds`;
-/*!40101 SET @saved_cs_client = @@character_set_client */;
-/*!40101 SET character_set_client = utf8 */;
-CREATE TABLE `aowow_sounds` (
- `id` smallint(5) unsigned NOT NULL,
- `cat` tinyint(3) unsigned NOT NULL,
- `name` varchar(100) COLLATE utf8mb4_unicode_ci NOT NULL,
- `cuFlags` int(10) unsigned NOT NULL,
- `soundFile1` smallint(5) unsigned DEFAULT NULL,
- `soundFile2` smallint(5) unsigned DEFAULT NULL,
- `soundFile3` smallint(5) unsigned DEFAULT NULL,
- `soundFile4` smallint(5) unsigned DEFAULT NULL,
- `soundFile5` smallint(5) unsigned DEFAULT NULL,
- `soundFile6` smallint(5) unsigned DEFAULT NULL,
- `soundFile7` smallint(5) unsigned DEFAULT NULL,
- `soundFile8` smallint(5) unsigned DEFAULT NULL,
- `soundFile9` smallint(5) unsigned DEFAULT NULL,
- `soundFile10` smallint(5) unsigned DEFAULT NULL,
- `flags` mediumint(8) unsigned NOT NULL,
- PRIMARY KEY (`id`),
- KEY `cat` (`cat`),
- KEY `name` (`name`)
-) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-/*!40101 SET character_set_client = @saved_cs_client */;
-
---
--- Table structure for table `aowow_sounds_files`
---
-
-DROP TABLE IF EXISTS `aowow_sounds_files`;
-/*!40101 SET @saved_cs_client = @@character_set_client */;
-/*!40101 SET character_set_client = utf8 */;
-CREATE TABLE `aowow_sounds_files` (
- `id` smallint(6) NOT NULL COMMENT '<0 not found in client files',
- `file` varchar(75) COLLATE utf8mb4_unicode_ci NOT NULL,
- `path` varchar(75) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT 'in client',
- `type` tinyint(1) unsigned NOT NULL COMMENT '1: ogg; 2: mp3',
- PRIMARY KEY (`id`)
-) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-/*!40101 SET character_set_client = @saved_cs_client */;
-
---
--- Table structure for table `aowow_source`
---
-
-DROP TABLE IF EXISTS `aowow_source`;
-/*!40101 SET @saved_cs_client = @@character_set_client */;
-/*!40101 SET character_set_client = utf8 */;
-CREATE TABLE `aowow_source` (
- `type` tinyint(4) unsigned NOT NULL,
- `typeId` mediumint(9) unsigned NOT NULL,
- `moreType` tinyint(4) unsigned DEFAULT NULL,
- `moreTypeId` mediumint(9) unsigned DEFAULT NULL,
- `moreZoneId` mediumint(9) unsigned DEFAULT NULL,
- `src1` tinyint(1) unsigned DEFAULT NULL COMMENT 'Crafted',
- `src2` tinyint(3) unsigned DEFAULT NULL COMMENT 'Drop (npc / object / item) (modeMask)',
- `src3` tinyint(3) unsigned DEFAULT NULL COMMENT 'PvP (g_sources_pvp)',
- `src4` tinyint(3) unsigned DEFAULT NULL COMMENT 'Quest (side)',
- `src5` tinyint(1) unsigned DEFAULT NULL COMMENT 'Vendor',
- `src6` tinyint(1) unsigned DEFAULT NULL COMMENT 'Trainer',
- `src7` tinyint(1) unsigned DEFAULT NULL COMMENT 'Discovery',
- `src8` tinyint(1) unsigned DEFAULT NULL COMMENT 'Redemption',
- `src9` tinyint(1) unsigned DEFAULT NULL COMMENT 'Talent',
- `src10` tinyint(1) unsigned DEFAULT NULL COMMENT 'Starter',
- `src11` tinyint(1) unsigned DEFAULT NULL COMMENT 'Event (special; not holidays) [not used]',
- `src12` tinyint(1) unsigned DEFAULT NULL COMMENT 'Achievemement',
- `src13` tinyint(3) unsigned DEFAULT NULL COMMENT 'Misc Source (sourceStringId)',
- `src14` tinyint(1) unsigned DEFAULT NULL COMMENT 'Black Market [not used]',
- `src15` tinyint(1) unsigned DEFAULT NULL COMMENT 'Disenchanted',
- `src16` tinyint(1) unsigned DEFAULT NULL COMMENT 'Fished',
- `src17` tinyint(1) unsigned DEFAULT NULL COMMENT 'Gathered',
- `src18` tinyint(1) unsigned DEFAULT NULL COMMENT 'Milled',
- `src19` tinyint(1) unsigned DEFAULT NULL COMMENT 'Mined',
- `src20` tinyint(1) unsigned DEFAULT NULL COMMENT 'Prospected',
- `src21` tinyint(1) unsigned DEFAULT NULL COMMENT 'Pickpocketed',
- `src22` tinyint(1) unsigned DEFAULT NULL COMMENT 'Salvaged',
- `src23` tinyint(1) unsigned DEFAULT NULL COMMENT 'Skinned',
- `src24` tinyint(1) unsigned DEFAULT NULL COMMENT 'In-Game Store [not used]',
- PRIMARY KEY (`type`,`typeId`)
-) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-/*!40101 SET character_set_client = @saved_cs_client */;
-
---
--- Table structure for table `aowow_spawns`
---
-
-DROP TABLE IF EXISTS `aowow_spawns`;
-/*!40101 SET @saved_cs_client = @@character_set_client */;
-/*!40101 SET character_set_client = utf8 */;
-CREATE TABLE `aowow_spawns` (
- `guid` int(11) NOT NULL COMMENT '< 0: vehicle accessory',
- `type` smallint(5) unsigned NOT NULL,
- `typeId` int(10) unsigned NOT NULL,
- `respawn` int(10) unsigned NOT NULL COMMENT 'in seconds',
- `spawnMask` tinyint(3) unsigned NOT NULL,
- `phaseMask` smallint(5) unsigned NOT NULL,
- `areaId` smallint(5) unsigned NOT NULL,
- `floor` tinyint(3) unsigned NOT NULL,
- `posX` float unsigned NOT NULL,
- `posY` float unsigned NOT NULL,
- `pathId` int(10) unsigned NOT NULL,
- PRIMARY KEY (`guid`,`type`,`floor`),
- KEY `type_idx` (`typeId`,`type`),
- KEY `zone_idx` (`areaId`),
- KEY `guid` (`guid`)
-) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-/*!40101 SET character_set_client = @saved_cs_client */;
-
---
--- Table structure for table `aowow_spawns_override`
---
-
-DROP TABLE IF EXISTS `aowow_spawns_override`;
-/*!40101 SET @saved_cs_client = @@character_set_client */;
-/*!40101 SET character_set_client = utf8 */;
-CREATE TABLE `aowow_spawns_override` (
- `type` smallint(5) unsigned NOT NULL,
- `typeGuid` mediumint(9) NOT NULL,
- `areaId` mediumint(8) unsigned NOT NULL,
- `floor` mediumint(8) unsigned NOT NULL,
- `revision` tinyint(3) unsigned NOT NULL COMMENT 'Aowow revision, when this override was applied',
- PRIMARY KEY (`type`, `typeGuid`) USING BTREE
-) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-/*!40101 SET character_set_client = @saved_cs_client */;
-
---
--- Table structure for table `aowow_spell`
---
-
-DROP TABLE IF EXISTS `aowow_spell`;
-/*!40101 SET @saved_cs_client = @@character_set_client */;
-/*!40101 SET character_set_client = utf8 */;
-CREATE TABLE `aowow_spell` (
- `id` mediumint(8) unsigned NOT NULL,
- `category` smallint(5) unsigned NOT NULL,
- `dispelType` tinyint(3) unsigned NOT NULL,
- `mechanic` tinyint(3) unsigned NOT NULL,
- `attributes0` int(10) unsigned NOT NULL,
- `attributes1` int(10) unsigned NOT NULL,
- `attributes2` int(10) unsigned NOT NULL,
- `attributes3` int(10) unsigned NOT NULL,
- `attributes4` int(10) unsigned NOT NULL,
- `attributes5` int(10) unsigned NOT NULL,
- `attributes6` int(10) unsigned NOT NULL,
- `attributes7` int(10) unsigned NOT NULL,
- `cuFlags` int(10) unsigned NOT NULL,
- `typeCat` smallint(6) NOT NULL,
- `stanceMask` int(10) unsigned NOT NULL,
- `stanceMaskNot` int(10) unsigned NOT NULL,
- `targets` mediumint(8) unsigned NOT NULL,
- `spellFocusObject` smallint(5) unsigned NOT NULL,
- `castTime` float unsigned NOT NULL,
- `recoveryTime` int(10) unsigned NOT NULL,
- `recoveryCategory` int(10) unsigned NOT NULL,
- `startRecoveryTime` mediumint(8) unsigned NOT NULL,
- `startRecoveryCategory` smallint(5) unsigned NOT NULL,
- `procChance` tinyint(3) unsigned NOT NULL,
- `procCharges` tinyint(3) unsigned NOT NULL,
- `procCustom` float NOT NULL,
- `procCooldown` smallint(6) unsigned NOT NULL,
- `maxLevel` tinyint(3) unsigned NOT NULL,
- `baseLevel` tinyint(3) unsigned NOT NULL,
- `spellLevel` tinyint(3) unsigned NOT NULL,
- `talentLevel` tinyint(3) unsigned NOT NULL,
- `duration` int(16) NOT NULL DEFAULT 0,
- `powerType` smallint(5) NOT NULL,
- `powerCost` smallint(5) unsigned NOT NULL,
- `powerCostPerLevel` tinyint(3) unsigned NOT NULL,
- `powerCostPercent` tinyint(3) unsigned NOT NULL,
- `powerPerSecond` smallint(5) unsigned NOT NULL,
- `powerPerSecondPerLevel` tinyint(3) unsigned NOT NULL,
- `powerGainRunicPower` smallint(5) unsigned NOT NULL,
- `powerCostRunes` smallint(5) unsigned NOT NULL,
- `rangeId` smallint(5) unsigned NOT NULL,
- `stackAmount` smallint(5) unsigned NOT NULL,
- `tool1` mediumint(8) unsigned NOT NULL,
- `tool2` mediumint(8) unsigned NOT NULL,
- `toolCategory1` tinyint(3) unsigned NOT NULL,
- `toolCategory2` tinyint(3) unsigned NOT NULL,
- `reagent1` mediumint(8) unsigned NOT NULL,
- `reagent2` mediumint(8) unsigned NOT NULL,
- `reagent3` mediumint(8) unsigned NOT NULL,
- `reagent4` mediumint(8) unsigned NOT NULL,
- `reagent5` mediumint(8) unsigned NOT NULL,
- `reagent6` mediumint(8) unsigned NOT NULL,
- `reagent7` mediumint(8) unsigned NOT NULL,
- `reagent8` mediumint(8) unsigned NOT NULL,
- `reagentCount1` tinyint(3) unsigned NOT NULL,
- `reagentCount2` tinyint(3) unsigned NOT NULL,
- `reagentCount3` tinyint(3) unsigned NOT NULL,
- `reagentCount4` tinyint(3) unsigned NOT NULL,
- `reagentCount5` tinyint(3) unsigned NOT NULL,
- `reagentCount6` tinyint(3) unsigned NOT NULL,
- `reagentCount7` tinyint(3) unsigned NOT NULL,
- `reagentCount8` tinyint(3) unsigned NOT NULL,
- `equippedItemClass` tinyint(4) NOT NULL,
- `equippedItemSubClassMask` int(11) NOT NULL,
- `equippedItemInventoryTypeMask` int(10) unsigned NOT NULL,
- `effect1Id` smallint(5) unsigned NOT NULL,
- `effect2Id` smallint(5) unsigned NOT NULL,
- `effect3Id` smallint(5) unsigned NOT NULL,
- `effect1DieSides` mediumint(9) NOT NULL,
- `effect2DieSides` mediumint(9) NOT NULL,
- `effect3DieSides` mediumint(9) NOT NULL,
- `effect1RealPointsPerLevel` float NOT NULL,
- `effect2RealPointsPerLevel` float NOT NULL,
- `effect3RealPointsPerLevel` float NOT NULL,
- `effect1BasePoints` int(11) NOT NULL,
- `effect2BasePoints` int(11) NOT NULL,
- `effect3BasePoints` int(11) NOT NULL,
- `effect1Mechanic` tinyint(3) unsigned NOT NULL,
- `effect2Mechanic` tinyint(3) unsigned NOT NULL,
- `effect3Mechanic` tinyint(3) unsigned NOT NULL,
- `effect1ImplicitTargetA` smallint(6) NOT NULL,
- `effect2ImplicitTargetA` smallint(6) NOT NULL,
- `effect3ImplicitTargetA` smallint(6) NOT NULL,
- `effect1ImplicitTargetB` smallint(6) NOT NULL,
- `effect2ImplicitTargetB` smallint(6) NOT NULL,
- `effect3ImplicitTargetB` smallint(6) NOT NULL,
- `effect1RadiusMin` smallint(5) unsigned NOT NULL,
- `effect1RadiusMax` smallint(5) unsigned NOT NULL DEFAULT 0,
- `effect2RadiusMin` smallint(5) unsigned NOT NULL,
- `effect2RadiusMax` smallint(5) unsigned NOT NULL DEFAULT 0,
- `effect3RadiusMin` smallint(5) unsigned NOT NULL,
- `effect3RadiusMax` smallint(5) unsigned NOT NULL DEFAULT 0,
- `effect1AuraId` smallint(5) unsigned NOT NULL,
- `effect2AuraId` smallint(5) unsigned NOT NULL,
- `effect3AuraId` smallint(5) unsigned NOT NULL,
- `effect1Periode` mediumint(8) unsigned NOT NULL,
- `effect2Periode` mediumint(8) unsigned NOT NULL,
- `effect3Periode` mediumint(8) unsigned NOT NULL,
- `effect1ValueMultiplier` float NOT NULL,
- `effect2ValueMultiplier` float NOT NULL,
- `effect3ValueMultiplier` float NOT NULL,
- `effect1ChainTarget` smallint(5) unsigned NOT NULL,
- `effect2ChainTarget` smallint(5) unsigned NOT NULL,
- `effect3ChainTarget` smallint(5) unsigned NOT NULL,
- `effect1CreateItemId` mediumint(8) unsigned NOT NULL,
- `effect2CreateItemId` mediumint(8) unsigned NOT NULL,
- `effect3CreateItemId` mediumint(8) unsigned NOT NULL,
- `effect1MiscValue` int(11) NOT NULL,
- `effect2MiscValue` int(11) NOT NULL,
- `effect3MiscValue` int(11) NOT NULL,
- `effect1MiscValueB` mediumint(9) NOT NULL,
- `effect2MiscValueB` mediumint(9) NOT NULL,
- `effect3MiscValueB` mediumint(9) NOT NULL,
- `effect1TriggerSpell` mediumint(9) NOT NULL,
- `effect2TriggerSpell` mediumint(9) NOT NULL,
- `effect3TriggerSpell` mediumint(9) NOT NULL,
- `effect1PointsPerComboPoint` mediumint(9) NOT NULL,
- `effect2PointsPerComboPoint` mediumint(9) NOT NULL,
- `effect3PointsPerComboPoint` mediumint(9) NOT NULL,
- `effect1SpellClassMaskA` int(10) unsigned NOT NULL,
- `effect2SpellClassMaskA` int(10) unsigned NOT NULL,
- `effect3SpellClassMaskA` int(10) unsigned NOT NULL,
- `effect1SpellClassMaskB` int(10) unsigned NOT NULL,
- `effect2SpellClassMaskB` int(10) unsigned NOT NULL,
- `effect3SpellClassMaskB` int(10) unsigned NOT NULL,
- `effect1SpellClassMaskC` int(10) unsigned NOT NULL,
- `effect2SpellClassMaskC` int(10) unsigned NOT NULL,
- `effect3SpellClassMaskC` int(10) unsigned NOT NULL,
- `effect1DamageMultiplier` float NOT NULL,
- `effect2DamageMultiplier` float NOT NULL,
- `effect3DamageMultiplier` float NOT NULL,
- `effect1BonusMultiplier` float NOT NULL,
- `effect2BonusMultiplier` float NOT NULL,
- `effect3BonusMultiplier` float NOT NULL,
- `iconId` smallint(5) unsigned NOT NULL DEFAULT 0,
- `iconIdBak` smallint(5) unsigned NOT NULL DEFAULT 0,
- `iconIdAlt` smallint(5) unsigned NOT NULL DEFAULT 0,
- `rankNo` tinyint(3) unsigned NOT NULL,
- `spellVisualId` smallint(5) unsigned NOT NULL,
- `name_loc0` varchar(85) COLLATE utf8mb4_unicode_ci NOT NULL,
- `name_loc2` varchar(85) COLLATE utf8mb4_unicode_ci NOT NULL,
- `name_loc3` varchar(85) COLLATE utf8mb4_unicode_ci NOT NULL,
- `name_loc4` varchar(85) COLLATE utf8mb4_unicode_ci NOT NULL,
- `name_loc6` varchar(91) COLLATE utf8mb4_unicode_ci NOT NULL,
- `name_loc8` varchar(50) COLLATE utf8mb4_unicode_ci NOT NULL,
- `rank_loc0` varchar(21) COLLATE utf8mb4_unicode_ci NOT NULL,
- `rank_loc2` varchar(24) COLLATE utf8mb4_unicode_ci NOT NULL,
- `rank_loc3` varchar(22) COLLATE utf8mb4_unicode_ci NOT NULL,
- `rank_loc4` varchar(22) COLLATE utf8mb4_unicode_ci NOT NULL,
- `rank_loc6` varchar(27) COLLATE utf8mb4_unicode_ci NOT NULL,
- `rank_loc8` varchar(29) COLLATE utf8mb4_unicode_ci NOT NULL,
- `description_loc0` mediumtext COLLATE utf8mb4_unicode_ci NOT NULL,
- `description_loc2` mediumtext COLLATE utf8mb4_unicode_ci NOT NULL,
- `description_loc3` mediumtext COLLATE utf8mb4_unicode_ci NOT NULL,
- `description_loc4` mediumtext COLLATE utf8mb4_unicode_ci NOT NULL,
- `description_loc6` mediumtext COLLATE utf8mb4_unicode_ci NOT NULL,
- `description_loc8` mediumtext COLLATE utf8mb4_unicode_ci NOT NULL,
- `buff_loc0` mediumtext COLLATE utf8mb4_unicode_ci NOT NULL,
- `buff_loc2` mediumtext COLLATE utf8mb4_unicode_ci NOT NULL,
- `buff_loc3` mediumtext COLLATE utf8mb4_unicode_ci NOT NULL,
- `buff_loc4` mediumtext COLLATE utf8mb4_unicode_ci NOT NULL,
- `buff_loc6` mediumtext COLLATE utf8mb4_unicode_ci NOT NULL,
- `buff_loc8` mediumtext COLLATE utf8mb4_unicode_ci NOT NULL,
- `maxTargetLevel` tinyint(3) unsigned NOT NULL,
- `spellFamilyId` tinyint(3) unsigned NOT NULL,
- `spellFamilyFlags1` int(10) unsigned NOT NULL,
- `spellFamilyFlags2` int(10) unsigned NOT NULL,
- `spellFamilyFlags3` int(10) unsigned NOT NULL,
- `maxAffectedTargets` tinyint(3) unsigned NOT NULL,
- `damageClass` tinyint(3) unsigned NOT NULL,
- `skillLine1` smallint(6) NOT NULL DEFAULT 0,
- `skillLine2OrMask` bigint(32) NOT NULL DEFAULT 0,
- `reqRaceMask` smallint(5) unsigned NOT NULL DEFAULT 0,
- `reqClassMask` smallint(5) unsigned NOT NULL DEFAULT 0,
- `reqSpellId` mediumint(8) unsigned NOT NULL DEFAULT 0,
- `reqSkillLevel` smallint(5) unsigned NOT NULL DEFAULT 0,
- `learnedAt` smallint(6) unsigned NOT NULL DEFAULT 0,
- `skillLevelGrey` smallint(6) unsigned NOT NULL DEFAULT 0,
- `skillLevelYellow` smallint(6) unsigned NOT NULL DEFAULT 0,
- `schoolMask` tinyint(3) unsigned NOT NULL,
- `spellDescriptionVariableId` tinyint(3) unsigned NOT NULL,
- `trainingCost` int(10) unsigned NOT NULL,
- PRIMARY KEY (`id`),
- KEY `category` (`typeCat`),
- KEY `spell` (`id`) USING BTREE,
- KEY `effects` (`effect1Id`,`effect2Id`,`effect3Id`),
- KEY `items` (`effect1CreateItemId`,`effect2CreateItemId`,`effect3CreateItemId`),
- KEY `iconId` (`iconId`)
-) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-/*!40101 SET character_set_client = @saved_cs_client */;
-
---
--- Table structure for table `aowow_spell_sounds`
---
-
-DROP TABLE IF EXISTS `aowow_spell_sounds`;
-/*!40101 SET @saved_cs_client = @@character_set_client */;
-/*!40101 SET character_set_client = utf8 */;
-CREATE TABLE `aowow_spell_sounds` (
- `id` smallint(5) unsigned NOT NULL COMMENT 'SpellVisual.dbc/id',
- `animation` smallint(5) unsigned NOT NULL,
- `ready` smallint(5) unsigned NOT NULL,
- `precast` smallint(5) unsigned NOT NULL,
- `cast` smallint(5) unsigned NOT NULL,
- `impact` smallint(5) unsigned NOT NULL,
- `state` smallint(5) unsigned NOT NULL,
- `statedone` smallint(5) unsigned NOT NULL,
- `channel` smallint(5) unsigned NOT NULL,
- `casterimpact` smallint(5) unsigned NOT NULL,
- `targetimpact` smallint(5) unsigned NOT NULL,
- `castertargeting` smallint(5) unsigned NOT NULL,
- `missiletargeting` smallint(5) unsigned NOT NULL,
- `instantarea` smallint(5) unsigned NOT NULL,
- `persistentarea` smallint(5) unsigned NOT NULL,
- `casterstate` smallint(5) unsigned NOT NULL,
- `targetstate` smallint(5) unsigned NOT NULL,
- `missile` smallint(5) unsigned NOT NULL COMMENT 'not predicted by js',
- `impactarea` smallint(5) unsigned NOT NULL COMMENT 'not predicted by js',
- PRIMARY KEY (`id`)
-) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='!ATTENTION!\r\nthe primary key of this table is NOT a spellId, but spellVisualId\r\n\r\ncolumn names from LANG.sound_activities';
-/*!40101 SET character_set_client = @saved_cs_client */;
-
---
--- Table structure for table `aowow_spelldifficulty`
---
-
-DROP TABLE IF EXISTS `aowow_spelldifficulty`;
-/*!40101 SET @saved_cs_client = @@character_set_client */;
-/*!40101 SET character_set_client = utf8 */;
-CREATE TABLE `aowow_spelldifficulty` (
- `normal10` mediumint(8) unsigned NOT NULL,
- `normal25` mediumint(8) unsigned NOT NULL,
- `heroic10` mediumint(8) unsigned NOT NULL,
- `heroic25` mediumint(8) unsigned NOT NULL,
- KEY `normal10` (`normal10`),
- KEY `normal25` (`normal25`),
- KEY `heroic10` (`heroic10`),
- KEY `heroic25` (`heroic25`)
-) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-/*!40101 SET character_set_client = @saved_cs_client */;
-
---
--- Table structure for table `aowow_spellfocusobject`
---
-
-DROP TABLE IF EXISTS `aowow_spellfocusobject`;
-/*!40101 SET @saved_cs_client = @@character_set_client */;
-/*!40101 SET character_set_client = utf8 */;
-CREATE TABLE `aowow_spellfocusobject` (
- `id` smallint(5) unsigned NOT NULL,
- `name_loc0` varchar(83) COLLATE utf8mb4_unicode_ci NOT NULL,
- `name_loc2` varchar(89) COLLATE utf8mb4_unicode_ci NOT NULL,
- `name_loc3` varchar(95) COLLATE utf8mb4_unicode_ci NOT NULL,
- `name_loc4` varchar(95) COLLATE utf8mb4_unicode_ci NOT NULL,
- `name_loc6` varchar(90) COLLATE utf8mb4_unicode_ci NOT NULL,
- `name_loc8` varchar(91) COLLATE utf8mb4_unicode_ci NOT NULL,
- PRIMARY KEY (`id`)
-) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-/*!40101 SET character_set_client = @saved_cs_client */;
-
---
--- Table structure for table `aowow_spelloverride`
---
-
-DROP TABLE IF EXISTS `aowow_spelloverride`;
-/*!40101 SET @saved_cs_client = @@character_set_client */;
-/*!40101 SET character_set_client = utf8 */;
-CREATE TABLE `aowow_spelloverride` (
- `id` bigint(20) NOT NULL,
- `spellId1` bigint(20) NOT NULL,
- `spellId2` bigint(20) NOT NULL,
- `spellId3` bigint(20) NOT NULL,
- `spellId4` bigint(20) NOT NULL,
- `spellId5` bigint(20) NOT NULL,
- PRIMARY KEY (`id`)
-) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-/*!40101 SET character_set_client = @saved_cs_client */;
-
---
--- Table structure for table `aowow_spellrange`
---
-
-DROP TABLE IF EXISTS `aowow_spellrange`;
-/*!40101 SET @saved_cs_client = @@character_set_client */;
-/*!40101 SET character_set_client = utf8 */;
-CREATE TABLE `aowow_spellrange` (
- `id` tinyint(3) unsigned NOT NULL,
- `rangeMinHostile` tinyint(3) unsigned NOT NULL,
- `rangeMinFriend` tinyint(3) unsigned NOT NULL,
- `rangeMaxHostile` smallint(5) unsigned NOT NULL,
- `rangeMaxFriend` smallint(5) unsigned NOT NULL,
- `rangeType` tinyint(3) unsigned NOT NULL,
- `name_loc0` varchar(27) COLLATE utf8mb4_unicode_ci NOT NULL,
- `name_loc2` varchar(27) COLLATE utf8mb4_unicode_ci NOT NULL,
- `name_loc3` varchar(27) COLLATE utf8mb4_unicode_ci NOT NULL,
- `name_loc4` varchar(27) COLLATE utf8mb4_unicode_ci NOT NULL,
- `name_loc6` varchar(27) COLLATE utf8mb4_unicode_ci NOT NULL,
- `name_loc8` varchar(27) COLLATE utf8mb4_unicode_ci NOT NULL,
- PRIMARY KEY (`id`)
-) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-/*!40101 SET character_set_client = @saved_cs_client */;
-
---
--- Table structure for table `aowow_spellvariables`
---
-
-DROP TABLE IF EXISTS `aowow_spellvariables`;
-/*!40101 SET @saved_cs_client = @@character_set_client */;
-/*!40101 SET character_set_client = utf8 */;
-CREATE TABLE `aowow_spellvariables` (
- `id` tinyint(3) unsigned NOT NULL,
- `vars` varchar(368) COLLATE utf8mb4_unicode_ci NOT NULL,
- PRIMARY KEY (`id`)
-) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-/*!40101 SET character_set_client = @saved_cs_client */;
-
---
--- Table structure for table `aowow_talents`
---
-
-DROP TABLE IF EXISTS `aowow_talents`;
-/*!40101 SET @saved_cs_client = @@character_set_client */;
-/*!40101 SET character_set_client = utf8 */;
-CREATE TABLE `aowow_talents` (
- `id` smallint(5) unsigned NOT NULL,
- `class` tinyint(3) unsigned NOT NULL,
- `petTypeMask` tinyint(3) unsigned NOT NULL,
- `tab` tinyint(3) unsigned NOT NULL,
- `row` tinyint(3) unsigned NOT NULL,
- `col` tinyint(3) unsigned NOT NULL,
- `spell` mediumint(8) unsigned NOT NULL,
- `rank` tinyint(3) unsigned NOT NULL,
- PRIMARY KEY (`id`,`rank`),
- KEY `spell` (`spell`),
- KEY `class` (`class`)
-) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-/*!40101 SET character_set_client = @saved_cs_client */;
-
---
--- Table structure for table `aowow_taxinodes`
---
-
-DROP TABLE IF EXISTS `aowow_taxinodes`;
-/*!40101 SET @saved_cs_client = @@character_set_client */;
-/*!40101 SET character_set_client = utf8 */;
-CREATE TABLE `aowow_taxinodes` (
- `id` smallint(5) unsigned NOT NULL,
- `mapId` smallint(6) unsigned NOT NULL,
- `posX` float unsigned NOT NULL,
- `posY` float unsigned NOT NULL,
- `type` tinyint(4) unsigned NOT NULL COMMENT 'usually NPC (1) but could support GOs (2)',
- `typeId` mediumint(9) unsigned NOT NULL,
- `reactA` tinyint(4) NOT NULL,
- `reactH` tinyint(4) NOT NULL,
- `name_loc0` varchar(46) COLLATE utf8mb4_unicode_ci NOT NULL,
- `name_loc2` varchar(62) COLLATE utf8mb4_unicode_ci NOT NULL,
- `name_loc3` varchar(55) COLLATE utf8mb4_unicode_ci NOT NULL,
- `name_loc4` varchar(55) COLLATE utf8mb4_unicode_ci NOT NULL,
- `name_loc6` varchar(63) COLLATE utf8mb4_unicode_ci NOT NULL,
- `name_loc8` varchar(50) COLLATE utf8mb4_unicode_ci NOT NULL,
- PRIMARY KEY (`id`)
-) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-/*!40101 SET character_set_client = @saved_cs_client */;
-
---
--- Table structure for table `aowow_taxipath`
---
-
-DROP TABLE IF EXISTS `aowow_taxipath`;
-/*!40101 SET @saved_cs_client = @@character_set_client */;
-/*!40101 SET character_set_client = utf8 */;
-CREATE TABLE `aowow_taxipath` (
- `id` smallint(5) unsigned NOT NULL,
- `startNodeId` smallint(6) unsigned NOT NULL,
- `endNodeId` smallint(6) unsigned NOT NULL,
- PRIMARY KEY (`id`)
-) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-/*!40101 SET character_set_client = @saved_cs_client */;
-
---
--- Table structure for table `aowow_titles`
---
-
-DROP TABLE IF EXISTS `aowow_titles`;
-/*!40101 SET @saved_cs_client = @@character_set_client */;
-/*!40101 SET character_set_client = utf8 */;
-CREATE TABLE `aowow_titles` (
- `id` tinyint(3) unsigned NOT NULL,
- `category` tinyint(3) unsigned NOT NULL,
- `cuFlags` int(10) unsigned NOT NULL,
- `gender` tinyint(3) unsigned NOT NULL,
- `side` tinyint(3) unsigned NOT NULL,
- `expansion` tinyint(3) unsigned NOT NULL,
- `src12Ext` mediumint(9) unsigned NOT NULL,
- `eventId` smallint(5) unsigned NOT NULL,
- `bitIdx` tinyint(3) unsigned NOT NULL,
- `male_loc0` varchar(33) COLLATE utf8mb4_unicode_ci NOT NULL,
- `male_loc2` varchar(35) COLLATE utf8mb4_unicode_ci NOT NULL,
- `male_loc3` varchar(37) COLLATE utf8mb4_unicode_ci NOT NULL,
- `male_loc4` varchar(37) COLLATE utf8mb4_unicode_ci NOT NULL,
- `male_loc6` varchar(34) COLLATE utf8mb4_unicode_ci NOT NULL,
- `male_loc8` varchar(37) COLLATE utf8mb4_unicode_ci NOT NULL,
- `female_loc0` varchar(33) COLLATE utf8mb4_unicode_ci NOT NULL,
- `female_loc2` varchar(35) COLLATE utf8mb4_unicode_ci NOT NULL,
- `female_loc3` varchar(39) COLLATE utf8mb4_unicode_ci NOT NULL,
- `female_loc4` varchar(39) COLLATE utf8mb4_unicode_ci NOT NULL,
- `female_loc6` varchar(35) COLLATE utf8mb4_unicode_ci NOT NULL,
- `female_loc8` varchar(41) COLLATE utf8mb4_unicode_ci NOT NULL,
- PRIMARY KEY (`id`),
- KEY `bitIdx` (`bitIdx`)
-) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-/*!40101 SET character_set_client = @saved_cs_client */;
-
---
--- Table structure for table `aowow_totemcategory`
---
-
-DROP TABLE IF EXISTS `aowow_totemcategory`;
-/*!40101 SET @saved_cs_client = @@character_set_client */;
-/*!40101 SET character_set_client = utf8 */;
-CREATE TABLE `aowow_totemcategory` (
- `id` tinyint(3) unsigned NOT NULL,
- `name_loc0` varchar(29) COLLATE utf8mb4_unicode_ci NOT NULL,
- `name_loc2` varchar(45) COLLATE utf8mb4_unicode_ci NOT NULL,
- `name_loc3` varchar(31) COLLATE utf8mb4_unicode_ci NOT NULL,
- `name_loc4` varchar(31) COLLATE utf8mb4_unicode_ci NOT NULL,
- `name_loc6` varchar(36) COLLATE utf8mb4_unicode_ci NOT NULL,
- `name_loc8` varchar(69) COLLATE utf8mb4_unicode_ci NOT NULL,
- `category` tinyint(3) unsigned NOT NULL,
- `categoryMask` int(10) unsigned NOT NULL,
- PRIMARY KEY (`id`)
-) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-/*!40101 SET character_set_client = @saved_cs_client */;
-
---
--- Table structure for table `aowow_user_ratings`
---
-
-DROP TABLE IF EXISTS `aowow_user_ratings`;
-/*!40101 SET @saved_cs_client = @@character_set_client */;
-/*!40101 SET character_set_client = utf8 */;
-CREATE TABLE `aowow_user_ratings` (
- `type` enum('Comment','Guide') COLLATE utf8mb4_unicode_ci NOT NULL,
- `entry` int NOT NULL DEFAULT '0',
- `userId` int unsigned NOT NULL DEFAULT '0' COMMENT 'User ID',
- `value` tinyint NOT NULL DEFAULT '0' COMMENT 'Rating Set',
- PRIMARY KEY (`type`,`entry`,`userId`),
- KEY `FK_acc_co_rate_user` (`userId`),
- CONSTRAINT `FK_userId` FOREIGN KEY (`userId`) REFERENCES `aowow_account` (`id`) ON UPDATE CASCADE
-) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;/*!40101 SET character_set_client = @saved_cs_client */;
-
---
--- Table structure for table `aowow_videos`
---
-
-DROP TABLE IF EXISTS `aowow_videos`;
-/*!40101 SET @saved_cs_client = @@character_set_client */;
-/*!40101 SET character_set_client = utf8 */;
-CREATE TABLE `aowow_videos` (
- `id` int(16) NOT NULL AUTO_INCREMENT,
- `type` smallint(5) unsigned NOT NULL,
- `typeId` mediumint(9) NOT NULL,
- `userIdOwner` int(10) unsigned DEFAULT NULL,
- `date` int(32) NOT NULL,
- `videoId` varchar(12) COLLATE utf8mb4_unicode_ci NOT NULL,
- `caption` mediumtext COLLATE utf8mb4_unicode_ci DEFAULT NULL,
- `status` int(8) NOT NULL,
- `userIdApprove` int(10) unsigned DEFAULT NULL,
- `userIdeDelete` int(10) unsigned DEFAULT NULL,
- PRIMARY KEY (`id`),
- KEY `type` (`type`,`typeId`),
- KEY `FK_acc_vi` (`userIdOwner`),
- CONSTRAINT `FK_acc_vi` FOREIGN KEY (`userIdOwner`) REFERENCES `aowow_account` (`id`) ON DELETE SET NULL ON UPDATE CASCADE
-) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=COMPACT;
-/*!40101 SET character_set_client = @saved_cs_client */;
-
---
--- Table structure for table `aowow_zones`
---
-
-DROP TABLE IF EXISTS `aowow_zones`;
-/*!40101 SET @saved_cs_client = @@character_set_client */;
-/*!40101 SET character_set_client = utf8 */;
-CREATE TABLE `aowow_zones` (
- `id` smallint(5) unsigned NOT NULL COMMENT 'Zone Id',
- `mapId` smallint(5) unsigned NOT NULL COMMENT 'Map Identifier',
- `mapIdBak` smallint(5) unsigned NOT NULL,
- `parentArea` smallint(6) unsigned NOT NULL,
- `category` tinyint(4) unsigned NOT NULL,
- `flags` int(11) unsigned NOT NULL,
- `cuFlags` int(10) unsigned NOT NULL,
- `faction` tinyint(2) unsigned NOT NULL,
- `expansion` tinyint(2) unsigned NOT NULL,
- `type` tinyint(2) unsigned NOT NULL,
- `maxPlayer` tinyint(4) NOT NULL,
- `itemLevelReqN` smallint(5) unsigned NOT NULL,
- `itemLevelReqH` smallint(5) unsigned NOT NULL,
- `levelReq` tinyint(3) unsigned NOT NULL,
- `levelReqLFG` tinyint(4) unsigned NOT NULL,
- `levelHeroic` tinyint(3) unsigned NOT NULL,
- `levelMin` tinyint(4) unsigned NOT NULL,
- `levelMax` tinyint(4) unsigned NOT NULL,
- `attunementsN` mediumtext COLLATE utf8mb4_unicode_ci NOT NULL COMMENT 'space separated; type:typeId',
- `attunementsH` mediumtext COLLATE utf8mb4_unicode_ci NOT NULL COMMENT 'space separated; type:typeId',
- `parentAreaId` smallint(5) unsigned NOT NULL,
- `parentX` float NOT NULL,
- `parentY` float NOT NULL,
- `name_loc0` varchar(120) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT 'Map Name',
- `name_loc2` varchar(120) COLLATE utf8mb4_unicode_ci NOT NULL,
- `name_loc3` varchar(120) COLLATE utf8mb4_unicode_ci NOT NULL,
- `name_loc4` varchar(120) COLLATE utf8mb4_unicode_ci NOT NULL,
- `name_loc6` varchar(120) COLLATE utf8mb4_unicode_ci NOT NULL,
- `name_loc8` varchar(120) COLLATE utf8mb4_unicode_ci NOT NULL,
- PRIMARY KEY (`id`)
-) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-/*!40101 SET character_set_client = @saved_cs_client */;
-
---
--- Table structure for table `aowow_zones_sounds`
---
-
-DROP TABLE IF EXISTS `aowow_zones_sounds`;
-/*!40101 SET @saved_cs_client = @@character_set_client */;
-/*!40101 SET character_set_client = utf8 */;
-CREATE TABLE `aowow_zones_sounds` (
- `id` smallint(5) unsigned NOT NULL,
- `ambienceDay` smallint(5) unsigned NOT NULL,
- `ambienceNight` smallint(5) unsigned NOT NULL,
- `musicDay` smallint(5) unsigned NOT NULL,
- `musicNight` smallint(5) unsigned NOT NULL,
- `intro` smallint(5) unsigned NOT NULL,
- `worldStateId` smallint(5) unsigned NOT NULL,
- `worldStateValue` smallint(6) NOT NULL,
- KEY `id` (`id`)
-) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-/*!40101 SET character_set_client = @saved_cs_client */;
-/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */;
-
-/*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
-/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;
-/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_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 */;
-/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;
-
--- Dump completed on 2018-03-28 11:47:47
--- MySQL dump 10.16 Distrib 10.2.10-MariaDB, for debian-linux-gnu (x86_64)
---
--- Host: localhost Database: aowow
--- ------------------------------------------------------
--- Server version 10.2.10-MariaDB-10.2.10+maria~xenial-log
-
-/*!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 utf8 */;
-/*!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' */;
-/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;
-
---
--- Dumping data for table `aowow_account`
---
-
-LOCK TABLES `aowow_account` WRITE;
-/*!40000 ALTER TABLE `aowow_account` DISABLE KEYS */;
-INSERT INTO `aowow_account` VALUES (0,0,'','','AoWoW','',0,0,0,0,'','',0,0,0,0,'','','',1,0,0,0,'');
-/*!40000 ALTER TABLE `aowow_account` ENABLE KEYS */;
-UNLOCK TABLES;
-
---
--- Dumping data for table `aowow_account_weightscales`
---
-
-LOCK TABLES `aowow_account_weightscales` WRITE;
-/*!40000 ALTER TABLE `aowow_account_weightscales` DISABLE KEYS */;
-INSERT INTO `aowow_account_weightscales` VALUES (1,0,'arms',1,'ability_rogue_eviscerate'),(2,0,'fury',1,'ability_warrior_innerrage'),(3,0,'prot',1,'ability_warrior_defensivestance'),(4,0,'holy',2,'spell_holy_holybolt'),(5,0,'prot',2,'ability_paladin_shieldofthetemplar'),(6,0,'retrib',2,'spell_holy_auraoflight'),(7,0,'beast',3,'ability_hunter_beasttaming'),(8,0,'marks',3,'ability_marksmanship'),(9,0,'surv',3,'ability_hunter_swiftstrike'),(10,0,'assas',4,'ability_rogue_eviscerate'),(11,0,'combat',4,'ability_backstab'),(12,0,'subtle',4,'ability_stealth'),(13,0,'disc',5,'spell_holy_wordfortitude'),(14,0,'holy',5,'spell_holy_guardianspirit'),(15,0,'shadow',5,'spell_shadow_shadowwordpain'),(16,0,'blooddps',6,'spell_deathknight_bloodpresence'),(17,0,'frostdps',6,'spell_deathknight_frostpresence'),(18,0,'frosttank',6,'spell_deathknight_frostpresence'),(19,0,'unholydps',6,'spell_deathknight_unholypresence'),(20,0,'elem',7,'spell_nature_lightning'),(21,0,'enhance',7,'spell_nature_lightningshield'),(22,0,'resto',7,'spell_nature_magicimmunity'),(23,0,'arcane',8,'spell_holy_magicalsentry'),(24,0,'fire',8,'spell_fire_firebolt02'),(25,0,'frost',8,'spell_frost_frostbolt02'),(26,0,'afflic',9,'spell_shadow_deathcoil'),(27,0,'demo',9,'spell_shadow_metamorphosis'),(28,0,'destro',9,'spell_shadow_rainoffire'),(29,0,'balance',11,'spell_nature_starfall'),(30,0,'feraltank',11,'ability_racial_bearform'),(31,0,'resto',11,'spell_nature_healingtouch'),(32,0,'feraldps',11,'ability_druid_catform');
-/*!40000 ALTER TABLE `aowow_account_weightscales` ENABLE KEYS */;
-UNLOCK TABLES;
-
---
--- Dumping data for table `aowow_account_weightscale_data`
---
-
-LOCK TABLES `aowow_account_weightscale_data` WRITE;
-/*!40000 ALTER TABLE `aowow_account_weightscale_data` DISABLE KEYS */;
-INSERT INTO `aowow_account_weightscale_data` VALUES (2,'exprtng',100),(2,'str',82),(2,'critstrkrtng',66),(2,'agi',53),(2,'armorpenrtng',52),(2,'hitrtng',48),(2,'hastertng',36),(2,'atkpwr',31),(2,'armor',5),(3,'sta',100),(3,'dodgertng',90),(3,'defrtng',86),(3,'block',81),(3,'agi',67),(3,'parryrtng',67),(3,'blockrtng',48),(3,'str',48),(3,'exprtng',19),(3,'hitrtng',10),(3,'armorpenrtng',10),(3,'critstrkrtng',7),(3,'armor',6),(3,'hastertng',1),(3,'atkpwr',1),(4,'int',100),(4,'manargn',88),(4,'splpwr',58),(4,'critstrkrtng',46),(4,'hastertng',35),(5,'sta',100),(5,'dodgertng',94),(5,'block',86),(5,'defrtng',86),(5,'exprtng',79),(5,'agi',76),(5,'parryrtng',76),(5,'hitrtng',58),(5,'blockrtng',52),(5,'str',50),(5,'armor',6),(5,'atkpwr',6),(5,'splpwr',4),(5,'critstrkrtng',3),(6,'mledps',470),(6,'hitrtng',100),(6,'str',80),(6,'exprtng',66),(6,'critstrkrtng',40),(6,'atkpwr',34),(6,'agi',32),(6,'hastertng',30),(6,'armorpenrtng',22),(6,'splpwr',9),(7,'rgddps',213),(7,'hitrtng',100),(7,'agi',58),(7,'critstrkrtng',40),(7,'int',37),(7,'atkpwr',30),(7,'armorpenrtng',28),(7,'hastertng',21),(8,'rgddps',379),(8,'hitrtng',100),(8,'agi',74),(8,'critstrkrtng',57),(8,'armorpenrtng',40),(8,'int',39),(8,'atkpwr',32),(8,'hastertng',24),(9,'rgddps',181),(9,'hitrtng',100),(9,'agi',76),(9,'critstrkrtng',42),(9,'int',35),(9,'hastertng',31),(9,'atkpwr',29),(9,'armorpenrtng',26),(10,'mledps',170),(10,'agi',100),(10,'exprtng',87),(10,'hitrtng',83),(10,'critstrkrtng',81),(10,'atkpwr',65),(10,'armorpenrtng',65),(10,'hastertng',64),(10,'str',55),(11,'mledps',220),(11,'armorpenrtng',100),(11,'agi',100),(11,'exprtng',82),(11,'hitrtng',80),(11,'critstrkrtng',75),(11,'hastertng',73),(11,'str',55),(11,'atkpwr',50),(12,'mledps',228),(12,'exprtng',100),(12,'agi',100),(12,'hitrtng',80),(12,'armorpenrtng',75),(12,'critstrkrtng',75),(12,'hastertng',75),(12,'str',55),(12,'atkpwr',50),(13,'splpwr',100),(13,'manargn',67),(13,'int',65),(13,'hastertng',59),(13,'critstrkrtng',48),(13,'spi',22),(14,'manargn',100),(14,'int',69),(14,'splpwr',60),(14,'spi',52),(14,'critstrkrtng',38),(14,'hastertng',31),(15,'hitrtng',100),(15,'shasplpwr',76),(15,'splpwr',76),(15,'critstrkrtng',54),(15,'hastertng',50),(15,'spi',16),(15,'int',16),(16,'mledps',360),(16,'armorpenrtng',100),(16,'str',99),(16,'hitrtng',91),(16,'exprtng',90),(16,'critstrkrtng',57),(16,'hastertng',55),(16,'atkpwr',36),(16,'armor',1),(17,'mledps',337),(17,'hitrtng',100),(17,'str',97),(17,'exprtng',81),(17,'armorpenrtng',61),(17,'critstrkrtng',45),(17,'atkpwr',35),(17,'hastertng',28),(17,'armor',1),(18,'mledps',419),(18,'parryrtng',100),(18,'hitrtng',97),(18,'str',96),(18,'defrtng',85),(18,'exprtng',69),(18,'dodgertng',61),(18,'agi',61),(18,'sta',61),(18,'critstrkrtng',49),(18,'atkpwr',41),(18,'armorpenrtng',31),(18,'armor',5),(19,'mledps',209),(19,'str',100),(19,'hitrtng',66),(19,'exprtng',51),(19,'hastertng',48),(19,'critstrkrtng',45),(19,'atkpwr',34),(19,'armorpenrtng',32),(19,'armor',1),(20,'hitrtng',100),(20,'splpwr',60),(20,'hastertng',56),(20,'critstrkrtng',40),(20,'int',11),(21,'mledps',135),(21,'hitrtng',100),(21,'exprtng',84),(21,'agi',55),(21,'int',55),(21,'critstrkrtng',55),(21,'hastertng',42),(21,'str',35),(21,'atkpwr',32),(21,'splpwr',29),(21,'armorpenrtng',26),(22,'manargn',100),(22,'int',85),(22,'splpwr',77),(22,'critstrkrtng',62),(22,'hastertng',35),(23,'hitrtng',100),(23,'hastertng',54),(23,'arcsplpwr',49),(23,'splpwr',49),(23,'critstrkrtng',37),(23,'int',34),(23,'frosplpwr',24),(23,'firsplpwr',24),(23,'spi',14),(24,'hitrtng',100),(24,'hastertng',53),(24,'firsplpwr',46),(24,'splpwr',46),(24,'critstrkrtng',43),(24,'frosplpwr',23),(24,'arcsplpwr',23),(24,'int',13),(25,'hitrtng',100),(25,'hastertng',42),(25,'frosplpwr',39),(25,'splpwr',39),(25,'arcsplpwr',19),(25,'firsplpwr',19),(25,'critstrkrtng',19),(25,'int',6),(26,'hitrtng',100),(26,'shasplpwr',72),(26,'splpwr',72),(26,'hastertng',61),(26,'critstrkrtng',38),(26,'firsplpwr',36),(26,'spi',34),(26,'int',15),(27,'hitrtng',100),(27,'hastertng',50),(27,'firsplpwr',45),(27,'shasplpwr',45),(27,'splpwr',45),(27,'critstrkrtng',31),(27,'spi',29),(27,'int',13),(28,'hitrtng',100),(28,'firsplpwr',47),(28,'splpwr',47),(28,'hastertng',46),(28,'spi',26),(28,'shasplpwr',23),(28,'critstrkrtng',16),(28,'int',13),(29,'hitrtng',100),(29,'splpwr',66),(29,'hastertng',54),(29,'critstrkrtng',43),(29,'spi',22),(29,'int',22),(30,'agi',100),(30,'sta',75),(30,'dodgertng',65),(30,'defrtng',60),(30,'exprtng',16),(30,'str',10),(30,'armor',10),(30,'hitrtng',8),(30,'hastertng',5),(30,'atkpwr',4),(30,'feratkpwr',4),(30,'critstrkrtng',3),(31,'splpwr',100),(31,'manargn',73),(31,'hastertng',57),(31,'int',51),(31,'spi',32),(31,'critstrkrtng',11),(32,'agi',100),(32,'armorpenrtng',90),(32,'str',80),(32,'critstrkrtng',55),(32,'exprtng',50),(32,'hitrtng',50),(32,'feratkpwr',40),(32,'atkpwr',40),(32,'hastertng',35);
-/*!40000 ALTER TABLE `aowow_account_weightscale_data` ENABLE KEYS */;
-UNLOCK TABLES;
-
---
--- Dumping data for table `aowow_announcements`
---
-
-LOCK TABLES `aowow_announcements` WRITE;
-/*!40000 ALTER TABLE `aowow_announcements` DISABLE KEYS */;
-INSERT INTO `aowow_announcements` VALUES (4,'compare','Help: Item Comparison Tool',0,'padding-left: 55px; background-image: url(STATIC_URL/images/announcements/help-small.png); background-position: 10px center',1,1,'First time? - Don\'t be shy! Just check out our [url=?help=item-comparison]Help page[/url]!','Première visite? - Ne soyez pas intimidé! Vous n\'avez qu\'à lire notre [url=?help=item-comparison]page d\'aide[/url] !','Euer erstes Mal? Nur keine falsche Scheu! Schaut einfach auf unsere [url=?help=item-comparison]Hilfeseite[/url]!','','¿Tu primera vez? ¡No seas vergonzoso! !Mira nuestra [url=?help=item-comparison]página de ayuda[/url]!','Впервые? Не стесняйтесь посетить нашу [url=?help=item-comparison]справочную страницу[/url]!'),(3,'profile','Help: Profiler',0,'padding-left: 80px; background-image: url(STATIC_URL/images/announcements/help-large.gif); background-position: 10px center',1,1,'[h3]First Time?[/h3]\n\nThe [b]Profiler[/b] tool lets you [span class=tip title=\"e.g. See how\'d you look as a different race, try different gear or talents, and more!\"]edit your character[/span], find gear upgrades, check your gear score, and more!\n\n[ul]\n[li][b]Right-click[/b] slots to change items, add gems/enchants, or find upgrades.[/li]\n[li]Use the [b]Claim character[/b] button to add your own characters to your [url=?user]user page[/url].[/li]\n[li]Save a modified character to your Aowow account by using the [b]Save as[/b] button.[/li]\n[li][b]Statistics[/b] will update in real time as you make tweaks.[/li]\n[/ul]\n\nFor more information, check out our extensive [url=?help=profiler]help page[/url]!','','[h3]Euer erster Besuch?[/h3]\n\nDas [b]Profiler[/b]-Werkzeug erlaubt es euch [span class=tip title=\"z.B. Seht, wie Ihr als anderes Volk aussehen würdet, probiert andere Ausrüstung oder Talente aus, und mehr!\"]euren Charakter zu bearbeiten[/span], besser Ausrüstung zu finden, eure Ausrüstungswertung zu vergleichen, und vieles mehr!\n\n[ul]\n[li][b]Rechts-klickt[/b] Plätze um Gegenstände zu tauschen, Edelsteine/Verzauberungen hinzuzufügen, oder bessere AUsrüstung zu finden.[/li]\n[li]Benutzt [b]Charakter beanspruchen[/b] um eure eigenen Charaktere Eurer [url=?user]Benutzerseite[/url] hinzuzufügen.[/li]\n[li]Speichert einen modifizierten Charakter in Eurem Aowow-Konto, indem Ihr [b]Speichern als[/b] benutzt.[/li]\n[li]Die [b]Statistiken[/b] aktualisieren sich in Echtzeit, während Ihr Änderungen durchführt.[/li]\n[/ul]\n\nWeitere Informationen findet Ihr auf unserer umfangreichen [url=?help=profiler]Hilfeseite[/url]!','','',''),(2,'profiler','Help: Profiler',0,'padding-left: 80px; background-image: url(STATIC_URL/images/announcements/help-large.gif); background-position: 10px center',1,1,'[h3]First Time?[/h3]\n\nThe [b]Profiler[/b] tool lets you [span class=tip title=\"e.g. See how\'d you look as a different race, try different gear or talents, and more!\"]edit your character[/span], find gear upgrades, check your gear score, and more!\n\n[ul]\n[li][b]Right-click[/b] slots to change items, add gems/enchants, or find upgrades.[/li]\n[li]Use the [b]Claim character[/b] button to add your own characters to your [url=?user]user page[/url].[/li]\n[li]Save a modified character to your Aowow account by using the [b]Save as[/b] button.[/li]\n[li][b]Statistics[/b] will update in real time as you make tweaks.[/li]\n[/ul]\n\nFor more information, check out our extensive [url=?help=profiler]help page[/url]!','','[h3]Euer erster Besuch?[/h3]\n\nDas [b]Profiler[/b]-Werkzeug erlaubt es euch [span class=tip title=\"z.B. Seht, wie Ihr als anderes Volk aussehen würdet, probiert andere Ausrüstung oder Talente aus, und mehr!\"]euren Charakter zu bearbeiten[/span], besser Ausrüstung zu finden, eure Ausrüstungswertung zu vergleichen, und vieles mehr!\n\n[ul]\n[li][b]Rechts-klickt[/b] Plätze um Gegenstände zu tauschen, Edelsteine/Verzauberungen hinzuzufügen, oder bessere AUsrüstung zu finden.[/li]\n[li]Benutzt [b]Charakter beanspruchen[/b] um eure eigenen Charaktere Eurer [url=?user]Benutzerseite[/url] hinzuzufügen.[/li]\n[li]Speichert einen modifizierten Charakter in Eurem Aowow-Konto, indem Ihr [b]Speichern als[/b] benutzt.[/li]\n[li]Die [b]Statistiken[/b] aktualisieren sich in Echtzeit, während Ihr Änderungen durchführt.[/li]\n[/ul]\n\nWeitere Informationen findet Ihr auf unserer umfangreichen [url=?help=profiler]Hilfeseite[/url]!','','','');
-/*!40000 ALTER TABLE `aowow_announcements` ENABLE KEYS */;
-UNLOCK TABLES;
-
---
--- Dumping data for table `aowow_articles`
---
-
-LOCK TABLES `aowow_articles` WRITE;
-/*!40000 ALTER TABLE `aowow_articles` DISABLE KEYS */;
-INSERT INTO `aowow_articles` VALUES (13,4,0,NULL,0,2,'[b][color=c4]Rogues[/color][/b] are a leather-clad melee class capable of dealing large amounts of damage to their enemies with very fast attacks. They are masters of stealth and assassination, passing by enemies unseen and striking from the shadows, then escaping from combat in the blink of an eye.\r\n\r\nThey are capable of using poisons to cripple their opponents, massively weakening them in battle. Rogues have a powerful arsenal of skills, many of which are strengthened by their ability to stealth and to incapacitate their victims.\r\n[ul]\r\n[li]Rogues can use a wide variety of melee weapons, such as daggers, fist weapons, one-handed maces, one-handed swords and one-handed axes.[/li]\r\n[li]By coating their weapons with [url=items=0.-3&filter=na=poison;ub=4]poison[/url] rogues can severely cripple or weaken their enemies.[/li]\r\n[li]When using [spell=1784] rogues will be unseen except by the most perceptive enemies.[/li]\r\n[/ul]',NULL),(14,1,0,NULL,0,2,'[b]Overview:[/b] The [b]humans[/b] are the most populous and the youngest race in Azeroth. The humans have become the [i]de facto[/i] leaders of the Alliance, with their youthful ambitions and resilience.\n\n[b]Capital City:[/b] The human seat of power is in the rebuilt city of [zone=1519].\n\n[b]Starting Zone:[/b] Humans begin questing in [zone=12].\n\n[b]Mounts:[/b] [npc=384] sells armoried ponies in Stormwind, and [npc=33307] at the Argent Tournament has a few distinct models.',NULL),(13,1,0,NULL,0,2,'[b][color=c1]Warriors[/color][/b] are a very powerful class, with the ability to tank or deal significant melee damage. The warrior\'s Protection tree contains many talents to improve their survivability and generate threat versus monsters. Protection warriors are one of the main tanking classes of the game.\n\nThey also have two damage-oriented talent trees - [icon name=ability_rogue_eviscerate][url=spells=7.1.26]Arms[/url][/icon] and [icon name=ability_warrior_innerrage][url=spells=7.1.256]Fury[/url][/icon], the latter of which includes the talent [spell=46917], which allows the warrior to wield two two-handed weapons at the same time! They are capable of strong melee AoE damage with spells such as [spell=845], [spell=1680], [spell=46924]. A warrior fights while in a specific [i]stance[/i], which grants him bonuses and access to different sets of abilities. He will use [spell=71] for tanking, and [spell=2457] or [spell=2458] for melee DPS.\n\n[ul]\n[li]All warriors can buff their raid or group by using a [i]shout[/i], [spell=6673] or [spell=469], and Fury warriors can provide the passive buff [spell=29801] which significantly increases the melee and ranged critical strike chance of his allies.[/li]\n[li]Warriors start out with only [spell=2457] at first, but learn [spell=71] at level 10 and [spell=2458] at level 30.[/li]\n[li]Warriors have numerous useful methods of getting to their target in a hurry! All warriors can use [spell=100] or [spell=20252] to reach an enemy and Protection warriors have [spell=3411], which allows them to intercept a friendly target and protect them from an attack.[/li]\n[/ul]',NULL),(13,2,0,NULL,0,2,'[b][color=c2]Paladins[/color][/b] bolster their allies with holy auras and blessing to protect their friends from harm and enhance their powers. Wearing heavy armor, they can withstand terrible blows in the thickest battles while healing their wounded allies and resurrecting the slain. In combat, they can wield massive two-handed weapons, stun their foes, destroy undead and demons, and judge their enemies with holy vengeance. Paladins are a defensive class, primarily designed to outlast their opponents.\n\nThe paladin is a mix of a melee fighter and a secondary spell caster. The paladin has a great deal of group utility due to the paladin\'s healing, blessings, and other abilities. Paladins can have one active aura per paladin on each party member and use specific blessings for specific players. Paladins are pretty hard to kill, thanks to their assortment of defensive abilities. They also make excellent tanks using their [spell=25780] ability.\n\n[ul]\n[li]Can effectively heal, tank, and deal damage in melee.[/li]\n[li]Has a wide selection of [url=spells=7.2&filter=na=blessing]Blessings[/url], [url=spells=7.2&filter=na=aura]Auras[/url], and other buffs.[/li]\n[li]Is the only class with access to a true invulnerability spell: [spell=642][/li]\n[/ul]',NULL),(14,2,0,NULL,0,2,'[b]Overview:[/b] The [b]orcs[/b] were originally a race of noble savages, residing on the world of Draenor. Unfortunately, The Burning Legion made use of them in an attempt to conquer Azeroth—they were infected with the daemonic blood of Mannoroth the Destructor, driven mad, and turned upon both the Draenei and the denizens of Azeroth. After losing the Second War, they were cut off from the corrupting influence of Mannoroth, and began to return to their shamanistic roots. Now, under the leadership of their new Warchief, the orcs are carving out a home for themselves in Azeroth.\n\n[b]Capital City:[/b] The orcs now reside in the city of [zone=1637], named after the deceased Orgrim Doomhammer, former Warchief of the Horde.\n\n[b]Starting Zone:[/b] Orcs begin questing in [zone=14].\n\n[b]Mounts:[/b] [npc=3362] in Orgrimmar sells a variety of wolves; [npc=33553] sells a few distinctive mounts at the Argent Tournament.',NULL),(13,3,0,NULL,0,2,'[b][color=c3]Hunters[/color][/b] are a very unique class in World of Warcraft. They are the sole non-magical ranged damage-dealers, fighting with bows and guns. Hunters have a number of different kinds of shots and stings, which can be used to debuff an enemy, and are capable of laying traps to deal damage or otherwise slow/incapacitate their enemy.\n\nA hunter will also tame his very own [url=pets]pet[/url] to aid them in combat. While they are not the only class which can use pet minions, the hunter\'s pet is unique in that each species has a particular type of talent tree, which the hunter can use to distribute points into various skills and passive abilities.\n\nIn addition, each species has a unique special ability. Hunters can seek out the most desirable pets based on their appearances or abilities, and if they spec deep enough into the [icon name=ability_hunter_beasttaming][url=spells=7.3.50]Beast Mastery[/url][/icon] tree they gain access to special, \"exotic\" beasts such as [pet=46] or [pet=39]!\n\n[ul]\n[li]Hunters have access to 23 (32 if [icon name=ability_hunter_beasttaming][url=spells=7.3.50]Beast Mastery[/url][/icon]) different [url=pets]species of pets[/url], featuring over 150 different appearances![/li]\n[li]Hunters have a number of survival-oriented skills which they can use to escape or avoid potential danger, such as [spell=5384] and [spell=781].[/li]\n[li][icon name=ability_hunter_swiftstrike][url=spells=7.3.51]Survival[/url][/icon] hunters can spec down the tree into [spell=53292], which allows them to provide the [spell=57669] buff to their party and raid members.[/li]\n[/ul]',NULL),(13,5,0,NULL,0,2,'[b][color=c5]Priests[/color][/b] are commonly considered one of the standard healing classes in World of Warcraft, as they have two talent specs that can be used to heal quite effectively.\n\nTheir [icon name=spell_holy_holybolt][url=spells=7.5.56]Holy[/url][/icon] tree includes talents which strongly boost the healing done to their allies, including spells that can be used to heal multiple players at once, such as [spell=48089]. The [icon name=spell_holy_wordfortitude][url=spells=7.5.613]Discipline[/url][/icon] tree, while still capable of significant raw healing output, focuses primarily on damage absorption and mitigation through use of [spell=48066] and procced shielding effects. Priests are also capable of very powerful ranged damage with their unique [icon name=spell_shadow_shadowwordpain][url=spells=7.5.78]Shadow[/url][/icon] abilities, and upon entering [spell=15473] will see a significant increase in their shadow damage while losing the ability to cast any Holy spells.\n\n[ul]\n[li]While the [icon name=spell_holy_wordfortitude][url=spells=7.5.613]Discipline[/url][/icon] talent tree is commonly used for healing, it also contains some powerful talents that can boost the priest\'s Holy damage, though [icon name=spell_shadow_shadowwordpain][url=spells=7.5.78]Shadow[/url][/icon] spells and abilities should be used primarily for DPS.[/li]\n[li]Priests provide of the most appreciated buffs in the game - [spell=48161], which grants an indispensable stamina buff to everyone in the raid. They can also buff both [spell=48073] and [spell=48169]![/li]\n[li]Shadow priests are an excellent utility class for any raid, providing the much-loved [spell=57669] buff to boost mana regeneration and can even heal their own party with [spell=15286]![/li]\n[/ul]',NULL),(13,6,0,NULL,0,2,'Introduced in the Wrath of the Lich King expansion, [b][color=c6]Death Knights[/color][/b] are World of Warcraft\'s first hero class. Death knights start at level 55 in a special, instanced zone unreachable by any other class: Acherus, the Ebon Hold, located in [zone=4298]. Here they will earn their talent points as quest rewards and even get a special summoned mount, the [spell=48778]!\n\nDeath knights have multiple very strong damage dealing options, as each of their talent trees can be specced to perform exceptionally well with a variety of melee abilities, spells and damage-over-time dealing diseases. They are also very capable tank classes, with both their Blood and Frost trees providing unique options - [icon name=spell_deathknight_bloodboil][url=spells=7.6.770]Blood[/url][/icon] dealing more with self-healing abilities and [icon name=spell_frost_frostnova][url=spells=7.6.771]Frost[/url][/icon] providing significant damage mitigation and strong AoE damage.\n\nDeath knights fight with a special buff active called a [i]presence[/i] (similar to a warrior\'s stances) which provides special bonuses to their roles. Death knights utilize a unique power system, with most spells costing either Runes, which are replenished throughout battle, or Runic Power, which can be generated by various abilities.\n\n[ul]\n[li][icon name=spell_deathknight_armyofthedead][url=spells=7.6.772]Unholy[/url][/icon] death knights can spec into [spell=52143], which makes their summoned Ghoul minion a permanent pet to aid in battle![/li]\n[li]The death knight class has its own special weapon enchanting ability called [spell=53428], which replaces the need for conventional weapon enchants.[/li]\n[li]Death knights are a very unique damage-dealing class in that their damage is dealt by both melee abilities [i]and[/i] spells![/li]\n[/ul]',NULL),(13,7,0,NULL,0,2,'[b][color=c7]Shamans[/color][/b] master elemental and nature magics and bring the most potential buffs to any group in the form of totems. A shaman can summon one totem of each element - earth, fire, air, and water - which appears at the shaman\'s feet and provides a buff to anyone in the shaman\'s party or raid within range of it. Some shaman totems, notably the fire ones, also do damage to opponents. The trick to playing any type of shaman is knowing which totems to cast under which circumstances to maximize the group\'s damage output and survivability.\n\nShamans are primarily spellcasters, although an [icon name=spell_nature_lightningshield][url=spells=7.7.373]Enhancement[/url][/icon] shaman likes to get close and personal and do damage within melee range. An enhancement shaman learns to [spell=30798] weapons and can use [spell=51533] to summon a pair of Spirit Wolves to aid in battle. Despite being primarily melee, [icon name=spell_nature_lightningshield][url=spells=7.7.373]Enhancement[/url][/icon] shamans can still gain some benefit from spellpower and can cast instant [spell=403] or heals with [spell=51530]. \n\n[icon name=spell_nature_lightning][url=spells=7.7.375]Elemental[/url][/icon] shamans stand back and cast fire and lightning spells to deal great amounts of damage. They can push back enemies with [spell=51490] and root all enemies in an area with[spell=51486]. They also bring [icon name=spell_fire_totemofwrath][url=spell=57722]Totem of Wrath[/url][/icon] and [spell=51470] as amazing spellcaster raid buffs. A shaman that choses [icon name=spell_nature_magicimmunity][url=spells=7.7.374]Restoration[/url][/icon] gains improved healing spells and can be a great raid or tank healer. Resto shamans are known for their powerful [spell=1064] ability and for providing a [spell=16190] to help their party\'s mana restoration. They also gain a powerful [spell=974], can use [spell=51886] to remove curses, and have an instant-cast direct heal plus heal over time effect called [spell=61295].\n\n[ul]\n[li]There are over twenty different totems a shaman can learn![/li]\n[li]Shamans can cast [spell=2825] (or [spell=32182]) to boost the entire group\'s damage and healing. This buff is unique and oft sought after for a raid group.[/li]\n[li]A shaman can turn into a [spell=2645] at level 16 and can even make it instant cast with [spell=16287]. This spell can be used in combat, but not indoors.[/li]\n[li]Shamans can only have one elemental shield - [spell=324] or [spell=52127] - on at a time. [spell=974], if the shaman knows it, can be cast on another player.[/li]\n[/ul]',NULL),(13,8,0,NULL,0,2,'[b][color=c8]Mages[/color][/b] wield the elements of fire, frost, and arcane to destroy or neutralize their enemies. They are a robed class that excels at dealing massive damage from afar, casting elemental bolts at a single target, or raining destruction down upon their enemies in a wide area of effect. Mages can also augment their allies\' spell-casting powers, summon food or drink to restore their friends, and even travel across the world in an instant by opening arcane portals to distant lands.\n\nWhen seeking someone to introduce monsters to a world of pain, the Mage is a good choice. With their elemental and arcane attacks, it\'s a safe bet something they can do won\'t be resisted by your chosen enemy. Damage is the name of the Mage game, and they do it well. Their arsenal includes some powerful buffs, debuffs, stuns, and snares, enabling them to dictate the terms of any fight.\n\n[ul]\n[li]Can [spell=42956] to restore their allies\' health and mana.[/li]\n[li]Are the only class that can create portals to transport other players. They cannot, however, summon players [i]from[/i] a distant location - that\'s a [icon name=class_warlock][color=c9]Warlock\'s[/color][/icon] job![/li]\n[li]Mages who use [item=50045] can have a permanent water elemental pet![/li]\n[/ul]',NULL),(13,9,0,NULL,0,2,'[b][color=c9]Warlocks[/color][/b] are masters of the demonic arts. Clothed in demonic styled cloth, they excel in using curses, firing bolts of fire or shadow, and summoning demons to help them in combat. Warlocks, while being excellent spell casters, also excel in supporting fellow allies by summoning other players or using ritual magics to conjure stones imbued with the power to heal.\r\n\r\nA warlock has very powerful abilities that, if used correctly, make them a very formidable opponent. Using their curses in combination with direct damage spells, Warlocks wreak havoc and destruction.\r\n\r\n[ul]\r\n[li]Can use a [spell=698] to summon another player to the portals location.[/li]\r\n[li]Are able to conjure [icon name=inv_stone_04][url=item=5509]Healthstones[/url][/icon] that have the ability to heal the user.[/li]\r\n[li]Can use curses on enemies to [url=spell=47865]weaken[/url] them or [url=spell=47864]damage[/url] them.[/li]\r\n[/ul]',NULL),(13,11,0,NULL,0,2,'[b][color=c11]Druids[/color][/b] are World of Warcraft\'s \"jack of all trades\" class -- that is, capable of performing in a variety of different roles and as such have one of the most varied playstyles. A druid can act as a healer, melee DPS, ranged DPS or a tank, utilizing a variety of [i]shapeshifting[/i] forms. As a druid levels up, he is able to learn new, powerful forms which he can cast to change into different creatures to suit their roles.\n\nAt lower levels, a druid will heal or ranged DPS in his caster form, but at later levels players who spec into the specialized trees will gain access to two special shapeshift forms for each different role.\n\nHealing druids will learn [spell=33891], which reduces the mana cost of their healing spells and grants a passive healing aura to their allies. Their ranged damage-dealing counterparts will learn [spell=24858], increasing their armor and granting a spell critical aura to their allies. There are also two feral form druid forms -- the mighty [spell=5487] (and at later level, [spell=9634]), a tanking-oriented form which provides additional armor and health and grants access to an arsenal of threat-building and damage mitigation abilities, and the rogue-like [spell=768] which is capable of significant melee DPS.\n\n[ul]\n[li]Druids learn their different forms through questing or training. Some shapeshifts are only learned via talents.[/li]\n[li]There are some shapeshifts that all druids can learn. [spell=5487] is obtained at level 10, [spell=1066] and [spell=783] at level 16, [spell=768] at level 20 and [spell=9634] at level 40.[/li]\n[li]Druids even have their own flying travel form! [spell=33943] can be trained at level 60, and [spell=40120] at level 71 provided the player has already trained [spell=34091].[/li]\n[li]Some druid shapeshifts are obtained via talents only - [spell=24858] can be obtained at level 40 when a player specs deep into the [icon name=spell_nature_starfall][url=spells=7.11.574]Balance[/url][/icon] tree, and [spell=33891] at level 50 after speccing deep into [icon name=spell_nature_healingtouch][url=spells=7.11.573]Restoration[/url][/icon].[/li]\n[li]Druids have their own, class-specific teleport ability that allows them to travel to and from [zone=493], which is handy when needing to train![/li]\n[li]Because feral druids do not actually swing weapons while in shapeshift forms, they instead gain a special statistic from any melee weapon they equip called \"feral attack power.\" This stat is a conversion of a weapon\'s DPS (damage per second) into an attack power-granting statistic which affects the cat or bear\'s damage output.[/li]\n[/ul]',NULL),(14,3,0,NULL,0,2,'[b]Overview:[/b] The [b]dwarves[/b] are a hardy race, hailing from Khaz Modan in the Eastern Kingdoms. Rumor has it they are descended from the Titans. There are three main clans of dwarves vying for power in Ironforge: the Bronzebeards, Wildhammers, and Dark Irons.\n\n[b]Capital City:[/b] The dwarves make their home in their ancestral seat of [zone=1537].\n\n[b]Starting Zone:[/b] Dwarves begin in [zone=1].\n\n[b]Mounts:[/b] [npc=1261] by the Amberstill Ranch sells rams, as well as [npc=33310] at the Argent Tournament.',NULL),(14,4,0,NULL,0,2,'[b]Overview:[/b] The [b]night elves[/b] are an ancient and mysterious race. They lived in Kalimdor for thousands of years, undisturbed until the world tree was sacrificed to halt the advance of the Burning Legion prior to the events of World of Warcraft.\n\n[b]Capital City:[/b] The night elf capital city is [zone=1657], situated in the branches of the world tree itself.\n\n[b]Starting Zone:[/b] Night Elves begin in [zone=141], learning about the recent political changes in Darnassus.\n\n[b]Mounts:[/b] [npc=4730] in Darnassus sells a variety of nightsabers, as well as [npc=33653] at the Argent Tournament.',NULL),(14,5,0,NULL,0,2,'[b]Overview:[/b] When the [b]undead[/b] scourge initially swept across Azeroth, they converted a number of members of the Alliance to the undead. When the combined forces of the orcs, elves, trolls, dwarves and humans began to fight back, though, [npc=36597]\'s hold on his forces began to weaken. A small faction of humans, known as the Forsaken, broke free of the Lich King\'s control.\n\nNow, free of the bonds of servitude as well as the troublesome emotions and connections of their human lives, the Forsaken have found a new home—with the Horde.\n\n[b]Capital City:[/b] The Forsaken reside in the [zone=1497], underneath the ruins of the former human city of Lordaeron.\n\n[b]Starting Zone:[/b] [zone=85] is the starting zone for Forsaken players--they are raised as second-generation Forsaken by val\'kyr and experience Sylvanas\' menacing new agenda firsthand.\n\n[b]Mounts:[/b] [npc=4731] in Tirisfal Glades sells numerous undead horses; [npc=33555] at the Argent Tournament sells a few distinct models.',NULL),(14,6,0,NULL,0,2,'[b]Overview:[/b] The [b]tauren[/b], a race with deep shamanistic roots, are longtime residents of Kalimdor. They have a deep and abiding love of nature, and the vast majority of them worship a deity known as the Earth Mother. \n\n[b]Capital City:[/b] The tauren reside in [zone=1638].\n\n[b]Starting Zone:[/b] Tauren begin questing in [zone=215].\n\n[b]Mounts:[/b] [npc=3685] sells numerous kodo mounts; [npc=33556] at the Argent Tournament sells a few distinctive models.',NULL),(14,7,0,NULL,0,2,'[b]Overview:[/b] The [b]gnomes[/b] are a quirky race, obsessed with gadgets and technology. They originally come from the city of [zone=721], which was destroyed by [npc=7937] in an attempt to save it from an invading army of troggs.\n\n[b]Capital City:[/b] The gnomes now make their home in [zone=1537]; they have made efforts to retake their beloved former city with [achievement=4786].\n\n[b]Starting Zone:[/b] Gnomes begin in [zone=1], but they have a very different quest sequence from Dwarves, covering Gnomeregan.\n\n[b]Mounts:[/b] [npc=7955] in Dun Morogh sells numerous mechanostriders, as well as [npc=33650] at the Argent Tournament.',NULL),(14,8,0,NULL,0,2,'[b]Overview:[/b] While there are many different tribes of [b]trolls[/b] scattered across Azeroth, only the [url=?faction=530]Darkspear Tribe[/url] has ever sworn allegiance to the Horde. The trolls originally lived in the Broken Isles, but were overrun by naga and murlocs and driven from their home. The orcs, led by [npc=4949], saved the Darkspear tribe from certain destruction and offered them amnesty among the Horde. In return, the Darkspear tribe swore fealty to the orcish warchief.\n\n[b]Capital City:[/b] The Darkspear Trolls live now in the Horde capital of [zone=1637].\n\n[b]Starting Zone:[/b] Trolls begin questing in [b]Echo Isles[/b].\n\n[b]Mounts:[/b] [npc=7952] in Sen\'jin Village sells numerous raptors; [npc=33554] at the Argent Tournament sells a few distinctive models.',NULL),(14,10,0,NULL,0,2,'[b]Overview:[/b] The [b]blood elves[/b] are a proud, haughty race, joining the Horde in Burning Crusade. They represent a faction of former high elves, split off from the rest of elven society; they are also survivors of Arthas\' assault on Silvermoon. Blood elves are fully dependent on magic, having revelled in its power for so long that they suffer horrible withdrawal if it were to be taken away.\n\n[b]Capital City:[/b] The blood elves have rebuilt [zone=3487].\n\n[b]Starting Zone:[/b] [zone=3430] is the starting zone for Blood Elves.\n\n[b]Mounts:[/b] [npc=16264] in Eversong Woods sells numerous hawkstriders; [npc=33557] at the Argent Tournament sells a few unique models.',NULL),(14,11,0,NULL,0,2,'[b]Overview:[/b] The [b]Draenei[/b] are followers of the Naaru and worshipers of the Holy Light. They originally hail from the distant world of Argus, fleeing after Sargeras tried to corrupt them. They then settled on the Orcish homeworld of Draenor, where after a period of peace, they were brutally murdered during Guldan\'s corruption of the Orcs. Finally they settled in Azeroth, to seek aid in their battle against the Burning Legion. Draenei were introduced in the Burning Crusade expansion.\n\n[b]Capital City:[/b] The Draenei have the seat of their power in the ruins of their once-great ship, [zone=3557].\n\n[b]Starting Zone:[/b] [zone=3524] and [zone=3525] cover the attempts of the Draenei to settle on their new island and deal with the inherent corruption present.\n\n[b]Mounts:[/b] [npc=17584] sells a variety of Elekks, as well as [npc=33657] at the Argent Tournament.',NULL),(8,21,0,NULL,0,2,'[minibox]\n[h2]Steamwheedle Cartel[/h2]\n[b]Booty Bay[/b]\n[faction=577]\n[faction=369]\n[faction=470]\n[/minibox]\n\n\n[b]Booty Bay[/b] is a large pirate town nestled into the cliffs surrounding a beautiful blue lagoon on the southern tip of [zone=33]. The city is entered by traversing through the bleached-white jaws of a giant shark.\n\nRun by the Blackwater Raiders who are closely associated with the Steamwheedle Cartel, the port offers facilities to any traveller passing through, regardless of their faction. Combined with the world renowned Salty Sailor Tavern, [event=15], numerous profession trainers, and vendors that sell everything from pets to diamond rings, it is one of the most popular locations in Azeroth.\n\n[npc=2496], ruler of this city, is hiring all the help he can get against the pesky [faction=87] and other threats of the city. He resides, together with the leader of the Blackwater Raiders, [npc=2487], at the top of the inn of Booty Bay.\n\nDue to the boat route from Booty Bay to Ratchet, players of all level ranges (mostly Horde, if lower level) can be expected to be found going about their business, although frequent visitors will more than likely fit in the 35 - 45 range. The quests available from the locals reflect this range nicely.\n\nThe water there occasionally has floating wreckages and schools of fish. The schools that are found most often are [item=6359], [item=6358], and [item=13422]. Fishing in the floating wreckages will also give you very high chances of fishing out chests and items, making Booty Bay an ideal place for fishing.\n\n[h3]Reputation[/h3]\nMost of the quests to raise reputation with Booty Bay are located in The Cape of Stranglethorn. Having a friendly or higher reputation will make the guards help you in case of initiated violence against you.\n\nIf you are Hated with Booty Bay, you can do the repeatable quest [quest=9259] to get back to Neutral.',NULL),(8,47,0,NULL,0,2,'[b]Ironforge[/b] is the faction associated with the capital city of the dwarves, [zone=1537]. [npc=2784] rules his kingdom of Khaz Modan from his throne room within the city, and the [npc=7937], leader of the gnomes, has temporarily had to settle down in Tinker Town after the recent fall of the gnome city [zone=133].\n\n[h3]History[/h3]\nIronforge is the ancient home of the dwarves. A marvel to the dwarves\' skill at shaping rock and stone, Ironforge was constructed in the very heart of the mountains, an expansive underground city home to explorers, miners, and warriors. Massive doors of rock protect the city in times of war, and lava from the mountain itself is redirected and distributed for heat, energy and smithing purposes. Before the Dark Iron Clan was banished from the city, eventually leading to the War of the Three Hammers, Ironforge was the commercial and social center of all the dwarven clans. It is now home to the Bronzebeard Clan. Many dwarven strongholds fell during the Second War between the Horde and the Alliance of Lordaeron, but the mighty city of Ironforge, nestled in the wintry peaks of [zone=1] and protected by its great gates, was never breached by the invading Horde.\n\nRelatively recently, Ironforge also became home to the Gnomeregan refugees. After the Third War, the gnomish city of Gnomeregan became overrun by troggs. Since then, a number of gnomes have settled in Ironforge, converting an area of that city to their liking, an area now known as Tinker Town.\n\nIronforge is one of most populated cities in the world, coming after the human city of [zone=1519], and housing 20,000 people.\n\nWhile the Alliance has been weakened by recent events, the dwarves of Ironforge, led by King Magni Bronzebeard, are forging a new future in the world.[h3]Reputation[/h3]\n[npc=14723] has the repeatable cloth reputation quests. As a reward for being exalted with Ironforge, non-dwarf players are able to ride [url=?items=15.5&filter=na=Ram;cr=93:92;crs=2:1;crv=0:0]rams[/url].\n\nSurrounding zones [zone=1], [zone=38] and [zone=11] contain the most quests for gaining reputation with Ironforge.',NULL),(8,54,0,NULL,0,2,'[b]Gnomeregan Exiles[/b] is the faction of gnomes who fled from their home, [zone=133] in [zone=1]. It was destroyed by the [url=?npcs=7&filter=na=Trogg]Trogg[/url] after a toxic invasion. Now a member of the Alliance, most are located in the Tinkertown section of the neighboring city [zone=1537], including leader [npc=7937].\n\n[h3]History[/h3]\nIt has been speculated that gnomes were formed as robots by the Titans, due to their inquisitive nature and technical skills.\n\nGnomes were an underground race of tinkers, residing in Gnomeregan until the troggs destroyed it. In this war, over 80% of the gnomish population was lost.\n\n[h3]Reputation[/h3]\n[npc=14724] has the repeatable cloth reputation quests. As a reward for being exalted with Ironforge, non-gnome dwarf players are able to ride [url=?items=15.5&filter=na=Mechanostrider;cr=93:92;crs=2:1;crv=0:0]mechanostriders[/url].\nSurrounding zone [zone=1] contain the most quests for gaining reputation with the Gnomeregan Exiles.',NULL),(8,59,0,NULL,0,2,'The [b]Thorium Brotherhood[/b] are an elite group of craftsmen who can reveal a number of epic recipes if you gain enough faction reputation with them. All players start off at Neutral reputation with them.\n\n[h3]History[/h3]\n\nThe [zone=51] is home to a group of exceptionally stout dwarves who have split from the Dark Iron Clan. On the cliffs overlooking the region called the Cauldron, in the far north of the Searing Gorge, the dwarves of the Thorium Brotherhood have established a base of operations, Thorium Point. From here, they keep a close eye on the Dark Iron dwarves\' activities in the Searing Gorge and beyond. Adventurers seeking out Thorium Point will find that the dwarves of the Thorium Brotherhood hold great rewards for those who aid them in their never ending struggle against their former brethren.\n\nThe Thorium Brotherhood comprises many exceptionally talented craftsmen, and the blacksmiths of the Brotherhood are rumored to be among the finest Azeroth has ever seen. They possess the knowledge required to make the arms and armaments of [npc=11502], the Fire Lord, but lack the manpower to obtain the materials required for the crafting. It is rumored that one member of the Thorium Brotherhood has been empowered to trade the dwarves\' fabled recipes and plans with those who can prove their loyalty to the Brotherhood. Of course, proving one\'s loyalty at some point may include venturing to the heart of the [zone=2717], the domain of Ragnaros, the Fire Lord himself, to supply the dwarves with the rare raw materials found there. A daunting task, no doubt, but gaining access to the Thorium Brotherhood\'s secrets should prove to be a reward well worth the effort.\n\n[h3]Reputation[/h3]\n\n[b]Neutral to Friendly[/b]\n\n[ul]\n[li]Turn in [item=18944], [item=3857] and either [item=4234], [item=3575], or [item=3356] to [npc=14624].[/li][/ul]\n[b]Friendly to Honored[/b]\n\n[ul]\n[li]Turn in [item=18945] to Master Smith Burninante.[/li][/ul]\n[b]Honored to Exalted[/b]\n\n[ul]\n[li]Turn in [item=11370] to [npc=12944].[/li]\n[li]Turn in [item=17012] to Lokhtos Darkbargainer.[/li]\n[li]Turn in [item=17010] to Lokhtos Darkbargainer.[/li]\n[li]Turn in [item=17011] to Lokhtos Darkbargainer.[/li]\n[li]Turn in [item=11382] to Lokhtos Darkbargainer.[/li][/ul]',NULL),(8,68,0,NULL,0,2,'[b]Undercity[/b] is the faction for the capital city of the Forsaken Undead, [zone=1497], ruled by Sylvanas Windrunner. It is located in [zone=85], at the northern edge of the Eastern Kingdoms. The city proper is located under the ruins of the historical City of Lordaeron. To enter it, you will walk through the ruined outer defenses of Lordaeron and the abandoned throneroom, until you reach one of three elevators guarded by two abominations.\n\n[h3]History[/h3]\nThe Undercity was originally simply a system of sewers, crypts, and catacombs beneath the Capital City of Lordaeron. After the city was destroyed by the Scourge, Arthas had the underground warren expanded and rebuilt. He originally intended for the Undercity to be his seat of power, from which he would rule the Plaguelands. However, shortly after the Third War ended, Arthas was forced to return to Northrend and save the Lich King. In his absence, [npc=10181] and her rebel Undead captured the ruins of the city. Soon after, she discovered the massive underground fortress, and decided to establish it as the main base of operations for the Undead Forsaken.\n\n[h3]Reputation[/h3]\n[npc=14729] has the Undercity repeatable cloth quests used by non-Undead Horde players to obtain the right to ride [url=?items=15.5&filter=na=Skeletal;cr=93:92;crs=2:1;crv=0:0]skeletal horses[/url] at exalted.\n\nSurrounding zones [zone=267], [zone=130], and Tirisfal Glades have the most quests to earn reputation with Undercity.',NULL),(8,69,0,NULL,0,2,'[b]Darnassus[/b] is the faction associated with [zone=1657], the capital city of the Night Elves. The high priestess, [npc=7999], resides in the Temple of the Moon, surrounded by other sisters of Elune. In the Cenarion Enclave, the [npc=3516] leads the [faction=609], often in direct opposition to his fellow druids in [zone=493] and Tyrande herself.\n\n[h3]History[/h3]\nIn the aftermath of the Third War, the night elves had to adjust to their mortal existence. Such an adjustment was far from easy, and there were many night elves who could not adjust to the prospects of aging, disease and frailty. Seeking to regain their immortality, a number of wayward druids conspired to plant a special tree that would reestablish a link between their spirits and the eternal world.\n\nWith [npc=15362] missing, Fandral Staghelm - the leader of those who wished to plant the new World Tree - became the new Arch-Druid. In no time at all, he and his fellow druids had forged ahead and planted the great tree, [zone=141], off the stormy coasts of northern Kalimdor. Under their care, the tree sprouted up above the clouds. Among the twilight boughs of the colossal tree, the wondrous city of Darnassus took root. However, the tree was not consecrated with nature\'s blessing and soon fell prey to the corruption of the Burning Legion. Now the wildlife and even the limbs of Teldrassil are tainted by a growing darkness.\n\n[h3]Reputation[/h3]\n[npc=14725] has the Darnassus repeatable [quest=7800] used by non-night elven Alliance players to obtain the right to ride [url=?items=15.5&filter=na=Reins+-Winterspring;ra=4;cr=93:92;crs=2:1;crv=0:0]night sabers[/url].[pad]Players who are at or close to level 44 looking to gain the favor of Darnassus should find and complete the quests of [zone=357]. The quests therein are associated with Darnassus and could prove to substantially increase your reputation should they all be completed.',NULL),(8,70,0,NULL,0,2,'The [b]Syndicate[/b] is a mostly Human criminal organization that operates primarily in the [zone=45] and the [zone=36], although a few small encampments are scattered in the [zone=267]. Their membership numbers around 3,000 persons.\n\nThey have three leaders: [npc=2423] (who took over from his father Aiden Perenolde), descendent of the original Lord of Alterac, who directs the Syndicate\'s actions in the Alterac Mountains from Strahnbrad; [npc=2597] directs Syndicate actions in Arathi Highlands from the main keep in the semi-abandoned fortress of Stromgarde; and Lady Beve Perenolde, daughter of Aiden Perenolde.\n\n[h3]History[/h3]\n\nDuring the Second War the Kingdom of Alterac, led by Lord Perenolde, was discovered to be in league with the Orcish Horde. Perenolde believed that a Horde victory was inevitable, and thus offered aid to the Horde by stirring up rebellions, attacking Alliance bases, and giving them supplies. When this treachery was discovered, the Alliance marched on Alterac and destroyed it. Perenolde and any nobles who went along with his plans were stripped of their titles and land. Many of the nobility managed to escape, however, and began plotting their revenge. Using their still sizable fortunes, the nobility hired a band of thieves and assassins, forming an organization known as the Syndicate.\n\nAt first the Syndicate\'s goal was just to spread chaos and disorder, striking from hidden bases in the Alterac Mountains. With the end of the Third War and the resultant chaos however, the leaders of the Syndicate saw their chance to return Alterac to its former power. They have now gained control of several outposts in the surrounding area including the sacked fortress of Durnholde Keep and a portion of the city of Stromgarde.\n\nThey are enemies of both the Alliance, whom they consider their mortal enemies, and the Horde, whom they consider mere brutes good for nothing but slave labor. As a result, the Syndicate is now hunted by both factions, with the [npc=10181], in particular, placing a bounty on their heads - guaranteeing that all captured Syndicate members will be summarily executed. In addition, [npc=4949] ordered a number of his agents, including [npc=2229], [npc=2239], [npc=2238] and their leader [npc=2316] to launch an investigation into the nature of the Syndicate and its activities, as well as to recover [item=3498], which belonged to a dear friend of his, [npc=18887] - a necklace now worn by Elysa, the mistress of Lord Aliden.\n\n[h3]Reputation[/h3]\n\nThe Syndicate as a faction in World of Warcraft is very odd in comparison to most factions in that the killing of the factions members will not lower your standing with the faction. For most players who are not a rogue, the only way for the Syndicate to appear on their Reputation Menu is to complete the quest [quest=8249], which is available to non-rogues. However, the quest requires [item=16885] ... which only rogues can obtain by pick-pocketing NPCs above level fifty, and those can only be traded to you - making it difficult to arrange such a transaction.\n\nCurrently there is only one known option to increase a player’s reputation with the Syndicate, and that is by killing members of the [faction=349] faction. There are no known rewards for increasing Syndicate reputation, and Ravenholdt-affiliated NPCs only give 1 Syndicate Reputation points, with the exception of [npc=13085], who gives 5 (although the corresponding loss of reputation with Ravenholdt is also five times as great). With all players starting at 32000/36000 hated with the faction, it would require killing 10,000 Ravenholdt NPCs to reach Neutral status with the faction; unfortunately, neutral status is the highest you can reach with the Syndicate, and if not to deter players further, none of the Ravenholdt NPCs drop loot.\n\n[b]WARNING[/b]: If you do decide to kill Ravenholdt NPCs, know that there is currently no way to restore your standings with Ravenholdt, if you do go below Neutral. The reason for the problem is that none of the quests that give Ravenholdt Reputation points will be available because none of the members from Ravenholdt will speak to you. This would mean its a permanent change and you will never be able to interact with any of the NPC loyal to Ravenholdt ever again. Also note that players start at 0/3000 reputation with Ravenholdt, and killing even one of their NPCs at this reputation level will forever prevent you from raising your reputation with them again.',NULL),(8,72,0,NULL,0,2,'[b]Stormwind[/b] is the faction associated with [zone=1519], the capital of the humans. It is located in the northwestern part of [zone=12]. The child king, [npc=1747], resides in Stormwind Keep, surrounded by his body guards and advisors, [npc=1748] (the regent), and [npc=1749]. The city is named for the occasional sudden squalls created by a ley line pattern in the mountains around the glorious city.\n\n[h3]History[/h3]\nDuring the First War, the Kingdom of Azeroth, including its capital, Stormwind Keep, was utterly destroyed by the Horde and its survivors fled to Lordaeron. After the orcs were defeated at the Dark Portal at the end of the Second War, it was decided that the city would be rebuilt, even surpassing its former grandeur. The nobles of Stormwind assembled a team of the most skilled and ingenious stonemasons and architects they could find. Under their direction, Stormwind was rebuilt in an amazingly short period of time. Now, at the end of the Third War, in the renamed Kingdom of Stormwind, it stands as one of the last bastions of human power left in the world. \n\nWith the fall of the northern kingdoms, Stormwind is by far the most populated city in the world. Boasting a population of two-hundred thousand people (predominantly human), it serves in many ways as the cultural and trade center of the Alliance, even with remote access to the sea. The humans living in the city are generally carefree and artistic, favoring light and colorful clothes, cuisine and art. It is home to the Academy of Arcane Sciences, the only wizarding school in Eastern Kingdoms, as well as SI:7, a rogue intelligence organization.\n\nHowever, the people of Stormwind find it difficult to accept Theramore\'s role as the home of the new Alliance, convinced not only that Stormwind should be the legitimate heir of Lordaeron\'s role in the past, but also that Theramore is doing little against the worsening situation within the Eastern Kingdoms.\n\n[h3]Reputation[/h3]\n[npc=14722] has the repeatable cloth quests to achieve a higher reputation with Stormwind. In return for exalted reputation, non-human players are able to ride horses.\n\nMost quests associated with Stormwind come from the surrounding areas of Elwynn Forest, [zone=40], and [zone=44].',NULL),(8,76,0,NULL,0,2,'[b]Orgrimmar[/b] is the faction for the capital city [zone=1637] of the orcs and trolls of the [faction=530]. Found at the northern edge of [zone=14], the imposing city is home to the orcish Warchief, [npc=4949].\n\n[h3]History[/h3]\nThrall led the orcs to the continent of Kalimdor, where they founded a new homeland with the help of their tauren brethren. Naming their new land Durotar after Thrall\'s murdered father, the orcs settled down to rebuild their once-glorious society. The demonic curse on their kind ended, the Horde changed from a warlike juggernaut into more of a loose coalition, dedicated to survival and prosperity rather than conquest. Aided by the noble tauren and the cunning trolls of the Darkspear tribe, Thrall and his orcs looked forward to a new era of peace in their own land. \n\nFrom there, they began the creation of the great warrior city, Orgrimmar. Named after the former Warchief, Orgrim Doomhammer, the new city was constructed in a short amount of time, with the aid of goblins, tauren, trolls, and the Mok\'Nathal Rexxar. Despite having some problems with the centaur, harpies, enraged thunder lizards, kobolds, evil orcish warlocks, quilboars, and unfortunately, the Alliance, Orgrimmar prospered in the end and became home to the orcs and Darkspear Trolls.\n\nToday, Orgrimmar lies at the base of a mountain between Durotar and [zone=16]. A warrior city indeed, it is home to countless amounts of orcs, trolls, tauren, and an increasing amount of Forsaken are now joining the city, as well as the Blood Elves who have recently been accepted into the Horde.\n\n[h3]Reputation[/h3]\n[npc=14726] has the Orgrimmar repeatable cloth quests used by non-orcish Horde players to obtain the right to ride [url=?items=15.5&filter=na=Wolf;cr=93:92;crs=2:1;crv=0:0]wolves[/url] at exalted.\n\nSurrounding areas Durotar and [zone=17] have the most quests for gaining reputation with Orgrimmar.',NULL),(8,81,0,NULL,0,2,'[b]Thunder Bluff[/b] is the faction of the Tauren capital city [zone=1638] located in the northern part of the region of [zone=215]. The whole of the city is built on bluffs several hundred feet above the surrounding landscape, and is accessible by elevators on the southwestern and northeastern sides.\n\n[h3]History[/h3]\nThe great city of Thunder Bluff lies atop a series of mesas that overlook the verdant grasslands of Mulgore. The once nomadic Tauren recently built the city as a center for trade caravans, traveling craftsmen and artisans of every kind. It was established by the mighty chief [npc=3057] after the Tauren, with help from the orcs, drove away the centaurs that originally inhabited Mulgore. Long bridges of rope and wood span the chasms between the mesas, topped with tents, longhouses, colorfully painted totems, and spirit lodges. The Tauren chief watches over the bustling city, ensuring that the united Tauren tribes live in peace and security.\n\n[h3]Reputation[/h3]\n[npc=14728] has the Thunder Bluff repeatable cloth quests used by non-tauren Horde players to obtain the right to ride [url=?items=15.5&filter=na=Kodo;cr=93:92;crs=2:1;crv=0:0]kodos[/url] at exalted.\n\nSurrounding zones Mulgore and [zone=17] have the most quests for gaining reputation with Thunder Bluff.',NULL),(8,87,0,NULL,0,2,'During the events leading up to and following the Third War, several criminal organizations appeared in Azeroth. The [b]Bloodsail Buccaneers[/b] appear to be one of these organizations, originating from the Bloodsail Hold on Plunder Isle and is where their ruler, Duke Falrevere holds court. They now plot to plunder and cripple the Steamwheedle Cartel controlled port town of [faction=21], currently under the protection of the Blackwater Raiders. It is likely the Bloodsail Buccaneers have come to take advantage of the town’s current loss of its fleet off the coast of the [zone=45], in which two of its ships were destroyed, and the remaining ship forced to find shelter in a cove, where its crew now fights to survive skirmishes with the Daggerspine Naga.\n\nIn preparation of the attack the Bloodsail Buccaneers have taken position in key locations near the town. Currently they have three ships anchored along the coastline south of Booty Bay, clear of the town’s defensive cannons, with camps also being built along the same coast in preparation of the attack. In addition, a scouting party has landed just west of the entrance to the town, reporting all activities, along with a compound being constructed along the road leading towards the town, likely to stop any re-enforcements from coming to help.\n\nBoth the Bloodsail Buccaneers and Blackwater Raiders seek to achieve their goals without having their forces engaged in battle, to this end each side now seek the aid of adventurers sympathetic to their cause.\n\n[h3]Reputation[/h3]\nThere is only one way to increase your reputation with the Bloodsail Buccaneers and that’s to unleash your wrath on any citizen of Booty Bay who can be found through out the Eastern Kingdoms. Below is a list of every citizen of Booty Bay and their reputation value. The amount gained with the Bloodsail Buccaneers is shown for a level 60 non-human. The amount lost for killing a citizen cannot be shown as it depends on your current level with Booty Bay and the importance of the person you kill. In addition to this what ever you lose with Booty Bay you will lose half of that in the other three goblin towns so if you lose 25 points in Booty Bay you will lose 12.5 points in [faction=470].\n\n[ul]\n[li][npc=4624]: 25 rep gained[/li]\n[li][npc=15088]: 25 rep gained[/li]\n[li][npc=2496]: 5 rep gained[/li]\n[li][npc=2636]: 5 rep gained[/li]\n[li][url=?npcs&filter=cr=3;crs=21;crv=0]Many more NPCs[/url]![/li]\n[/ul]\n\nThe fastest way to increase you reputation with the Bloodsail Buccaneers is to kill Booty Bay Bruisers. At first it may seem a simple task as the guards don\'t appear as threatening as the other monsters a player faces within the game. However, the guards are highly equipped to neutralize players of any class, to prevent people from attacking each other while in the town. What gives the Booty Bay Bruiser the advantage is several factors, one of them being their ability to use nets to lock you in place, preventing you from escaping. Another is the fact that they spawn every time you attack a citizen of the city or if you’re under Unfriendly status with Booty Bay the Bruisers can spawn if you enter a building, because of this players can soon find them selves swarmed by Bruisers.\n\nYet, theses are just the minor problems, in comparison to the Bruiser’s strongest ability, once it pulls out its gun its unlikely you will live, if you do not escape fast enough. Each time a guard shoots you, the attack throws you back, much like an Ogre hammer attack; the difference here is that the Bruiser can shoot in quick succession causing chain throw backs. A player can literally be thrown from one side of the town to the other, preventing you from attacking. More often you will find your self being forced into a corner, unable to move and unable to attack with each spell being interrupted by the Bruiser’s attack. Because the Bruisers do not put their guns away once they are out, the best course of action is to run away. \n\nThrough trial and error most people have discovered a safe place to kill Booty Bay Bruisers. If you follow the tunnel leading into the town, the path to your left that leads to the Blacksmith house is the ideal place to kill the guards. Only two guards patrol this path and normally don’t pass each other that closely, allowing both to be dispatched separately. Once they are gone, one can simply enter the first build on the path to cause a guard to spawn if they are below Unfriendly, if not they can simply attack one of the two NPC in the build, both of which are not high in level. Doing this a player should be able to kill 2 to 4 Bruisers before the two patrolling Bruisers re-spawn. On average a player doing this can kill about 30 to 40 Booty Bay Bruisers gaining about 800 reputation points with the pirates. The Bruisers here don’t appear to pull out their guns, but if you find your self in a bad situation, you can jump over the railing running along the path to the waters below, to escape.\n\n[h3]Rewards[/h3]\nBecoming friendly with the Bloodsail Buccaneers will grant you access to the following items:\n\n[ul]\n[li][item=12185] - Summons a [npc=11236][/li]\n[li][item=22742][/li]\n[li][item=22743][/li]\n[li][item=22745][/li]\n[/ul]\n\nYou will need Honored with the Bloodsail Buccaneers for [achievement=2336].',NULL),(8,92,0,NULL,0,2,'[b]Gelkis[/b] are a tribe of centaur who have made their home in the southmost parts of [zone=405]. They are mortal enemies of the [faction=93], a brother tribe also located in southern Desolace. The founding leader, or Khan, of the Gelkis was [npc=13741], second of the alleged offspring of Zaetar and Theradras. They are presently lead by [npc=5602] and the clan representative [npc=5397]. \n\nThe Gelkis hold no alliance with their brother tribes, but have been known to act both hostile and passive towards members of the Alliance and Horde.\n\n[h3]History[/h3]\nOriginally lead by the Second Khan Gelk, the Magram situated themselves in the southernmost regions of Desolace when the centaur divided into five tribes and have remained there ever since. \n\nWhen the Gelkis tribe spoke out against Khan Magra of the Magram\'s notion that strength was essential and the tribe’s survival depended on their fighting spirit, arguing that Theradras always watches over the centaur and will keep the tribes safe and alive, an eternal feud between the two tribes was born. \n\nAs such the Gelkis are more civilized - or as close as centaur can come to civilized - than their brethren, with an organised social structure and a firm grasp of the Common tongue. While the Magram only respect strength, the Gelkis respect nature and their birthmother Theradras, calling upon her protection and the power of earth to maintain their existence. Though the Magram view this as weak it would seem to be an erroneous view, as Earth Elementals can be sighted in Gelkis Village, putting an end to unwelcome intruders alongside their centaur masters.\n\n[h3]Reputation[/h3]\nOne of the two factions situated in Desolace, you are required to have a certain amount of reputation with the Gelkis in order to start their quests. Reputation for the Gelkis can be gained by killing [url=?npcs=7&filter=na=Magram]Magram monsters[/url]. When killing Magram monsters, you gain 20 reputation with Gelkis and lose 100 with the Magram tribe.',NULL),(8,93,0,NULL,0,2,'[b]Magram[/b] are a tribe of centaur who have made their home in the southeastern parts of [zone=405]. They are mortal enemies of the [faction=92], a brother tribe also located in southern Desolace. The founding leader, or Khan, of the Magram was [npc=13740], third of the alleged offspring of Zaetar and Theradras. They are presently lead by [npc=5601] and the clan representative [npc=5398]. \n\nThe Magram hold no alliance with their brother tribes, but have been known to act both hostile and passive towards members of the Alliance and Horde.\n\n[h3]History[/h3]\nOriginally lead by the Third Khan Magra, the Magram situated themselves against the mountain ranges of Desolace when the centaur divided into five tribes and have remained there ever since. \n\nBefore the death of Magra, he installed the idea that strength was essential and the tribe’s survival depended on their fighting spirit. When their brother tribe of Gelkis centaur spoke out against this notion, arguing that Theradras always watches over the centaur and will keep the tribes safe and alive, an eternal feud between the two tribes was born. \n\nThe life-long pursuit of strength has carried on through the Khans of Magram to this day, turning them violent and determined. To solidify their title as the strongest the tribe still fights fiercely to weaken or destroy their brother clans, viewing the Kolkar as weak, the Gelkis as nothing more than a nuisance, and the Maraudine as a formidable enemy. \n\nIt can be assumed that the Magram’s culture has developed into revolving around strength worship above all else. When compared to the Gelkis, the Magram hold very primitive forms of speech and social structure. For example, their grasp of common is limited and the position of Khan would likely be sought through a death match of sorts.\n\n[h3]Reputation[/h3]\nOne of the two factions situated in Desolace, you are required to have a certain amount of reputation with the Magram in order to start their quests. Reputation for the Magram can be gained by killing [url=?npcs=7&filter=na=Gelkis]Gelkis monsters[/url]. When killing Gelkis monsters, you gain 20 reputation with Magram and lose 100 with the Gelkis tribe.',NULL),(8,270,0,NULL,0,2,'[b]Zandalar Tribe[/b] trolls have come to Yojamba Isle in [zone=33] in the effort to recruit help against the resurrected Blood God and his Atal\'ai Priests in [zone=19] and in the [zone=1417].\n\n[h3]History[/h3]\nThe Zandalarians were the earliest known trolls, the first tribe from which all tribes originated. Over time two distinct troll empires emerged - the Amani and the Gurubashi. They existed for thousands of years until the coming of the Night Elves, who warred with them and eventually drove both empires into exile. \n\nFollowing the Great Sundering, the defeated Gurubashi grew ever more desperate to eke out a living. Searching for a means to survive, they enlisted the aid of the savage [npc=14834], also known as the Soulflayer. Hakkar grew into a merciless oppressor who demanded daily sacrifices from his devotees, and so in time the Gurubashi turned on their dark master. The strongest tribes (including the Zandalar) banded together to defeat Hakkar and his loyal troll priests, the Atal\'ai. The united tribes narrowly defeated the Blood God and cast out the Atal\'ai... despite their victory, however, the Gurubashi Empire soon fell. \n\nIn recent years the exiled Atal\'ai priests have discovered that Hakkar\'s physical form can only be summoned within the ancient and once-deserted capital of the Gurubashi Empire, Zul\'Gurub. Unfortunately, the priests have met with success in their quest to call forth Hakkar—reports confirm the presence of the dreaded Soulflayer in the heart of the ruins. \n\nAnd so the Zandalar tribe has arrived on the shores of Azeroth to battle Hakkar once again. But the Blood God has grown increasingly powerful, bending several tribes to his will and even commanding the avatars of the Primal Gods— Bat, Panther, Tiger, Spider and Snake. With the tribes splintered, the Zandalarians have been forced to recruit champions from Azeroth\'s varied and disparate races to battle, and hopefully once again defeat, the Soulflayer.\n\n[h3]Reputation[/h3]\nReputation with the Zandalar Tribe is gained from killing trash and bosses in Zul\'Gurub as well as repeatable and special quests which require instance-dropped items to complete. Each full run of Zul\'Gurub gives approximately 2,500-3,000 reputation.\n\nBefore the Burning Crusade, the main reason for gaining reputation with the tribe were the [url=?items=0.6&filter=na=Zandalar]shoulder[/url], [url=?items=0.6&filter=minrl=60;maxrl=60;cr=18:107;crs=4:0;crv=0:to+a+leg+or+head+slot+item]head and leg[/url] slot item enchants. As well, there were popular alchemy and enchanting recipes that many end-game guilds sought after. All rewarded items from the item set within Zul\'Gurub required a set level of reputation.',NULL),(8,349,0,NULL,0,2,'[b]Ravenholdt[/b] is a guild of thieves and assassins that welcomes only those of extraordinary prowess into its fold. They are diametrically opposed to the [faction=70], and are a rogue-only faction as all quests are rogue-only quests. The exception is the quest [quest=8249], which is available to non-rogues, but they would require the help of a rogue to get the items for the quest. [b]Ravenholdt Manor[/b], the faction\'s headquarters, is located in [zone=36], but to get there you have to come from the northeast corner of [zone=267].\n\n[h3]Reputation[/h3]\nAll Syndicate [url=?search=Syndicate#npcs]humanoids[/url] give 1-5 reputation points per kill depending on your current level. As well, there are a few quests that increase your reputation, but your primary method to raise your reputation is from the repeatable quests for turning in pickpocketed items.\n\nYou start off at 0/3000 Neutral with Ravenholdt, meaning if you kill any Ravenholdt NPCs before raising your reputation by at least 5, you will become Unfriendly and be unable to raise your reputation any higher ever again. To raise your reputation from Neutral to Friendly, the repeatable quest [quest=6701] is available. You will have to turn in 11-12 [item=17124] and once you are Friendly, this quest is no longer an option. From Neutral to Friendly you can also deliver five [item=16885] for Junkboxes Needed.\n\nTo raise your reputation beyond Friendly, the only choice is the repeatable quest Junkboxes Needed. There is no known faction reward for obtaining Friendly, Honored, Revered or Exalted, except that the guards speak to you with more respect. However, Exalted is required to obtain the Feat of Strength [achievement=2336].',NULL),(8,369,0,NULL,0,2,'[minibox]\n[h2]Steamwheedle Cartel[/h2]\n[faction=21]\n[faction=577]\n[b]Gadgetzan[/b]\n[faction=470]\n[/minibox]\n\n[b]Gadgetzan[/b] is the faction of the city Gadgetzan, which is home to goblinhood\'s finest engineers, alchemists and merchants and is the only spot of civilization in the entire desert. Rising out of the northern [zone=440] desert like an oasis, Gadgetzan is the headquarters of the Steamwheedle Cartel, the largest of the Goblin Cartels. The Goblins believe in profit above loyalty, thus Gadgetzan is considered neutral territory in the Horde/Alliance conflict.\n\n[h3]History[/h3]\nAlthough the goblins\' neutrality is almost universally acknowledged, there are still those who seek to sow chaos and anarchy. For Gadgetzan, this comes in the form of the Wastewander bandits, a gang of miscreants who have occupied the Waterspring Field and Noonshade Ruins of northeast Tanaris. Few goblins care about ancient ruins (unless they have treasure) – for all they care, the bandits can have the old blocks of stone. \n\nHowever, the Waterspring Field is vital to the goblins\' survival in the desert, providing them with the liquid gold of the desert. Water towers out in the field were constructed under the blazing heat of the desert sun by the backbreaking work of their slaves, and by Az, the goblins aren\'t going to give up their hard earned towers that easily. However, the Bruisers need to stay in town to keep the gnomes\' collective Napoleonic-complex from getting out of hand and to stop the seemingly endless dueling among the various visitors from disrupting business. Therefore, it falls to brave mercenaries from all corners of the world to help the goblins in their time of utmost need.\n\n[h3]Reputation[/h3]\nKilling the [url=?npcs=7&filter=na=Southsea]Southsea[/url] and [url=?npcs=7&filter=na=Wastewander]Wastewander[/url] monsters will increase your reputation with the Steamwheedle Cartel. Having a friendly or higher reputation will make the guards help you in case of initiated violence against you. Having an exalted reputation means that the guards will never attack you even if you initiate attacks on the opposite faction.\n\nMost of the quests associated with the Gadgetzan faction are located in Tanaris.\n\nIf you are Hated with Gadgetzan, you can do the repeatable quest [quest=9268] to obtain Neutral.',NULL),(8,470,0,NULL,0,2,'[minibox]\n[h2]Steamwheedle Cartel[/h2]\n[faction=21]\n[faction=577]\n[faction=369]\n[b]Ratchet[/b]\n[/minibox]\n\n[b]Ratchet[/b], the faction of the city Rachet on Kalimdor’s central east coast in [zone=17], is run by goblins and shows it. Its streets sprawl in every direction, and the architecture shows no consistency or common vision. It is a city of entertainment and trade, where anything that anyone would ever want to buy — and plenty of things that no one ever wants to buy — is on sale.\n\nRatchet is currently run by a corporate group known as the Steamwheedle Cartel a splinter group from the Venture Company, who first built the port town for trading with [zone=1637]. It is initially a neutral faction to both Horde and Alliance. A ferry conveniently connects Ratchet to Booty Bay.\n\n[h3]History[/h3]\nBuilt from equal parts of industry and decadence, the goblin port city of Ratchet sprawls along nearly a mile of of coastline where the eastern Barrens poke between [zone=14] and the [zone=15] to the sea. Ratchet is the pride of the goblins, a trade city where you can find almost anything your heart desires - and if something is not in stock, you can bet the goblins can order it. Ratchet also had regular ferries that traversed the safe though roundabout route to the island stronghold of Theramore to the south.\n\nRatchet is a city where creatures who were once the butt of jokes now reign supreme. Its streets wander without rhyme or reason through neighborhoods dedicated to one activity: commerce. Ramshackle warehouses stand next to stately stone homes. Fine shops press cheek to jowl with rude huts. Wares of every type imaginable - and some beyond the imagination - are on display in markets and in exclusive boutiques.\n\nGoblins welcome anyone with gold or items of value and a willingness to trade them for their wares and services. Merchants throng the marketplaces each day, selling everything from silks to slaves, and even at night the stores lining the twisting streets and alleys remain open for business. Those with the money can listen to skilled musicians while drinking fine ales and eating food prepared by expert chefs. For those with earthier tastes, the streets along the wharf teem with whorehouses, taprooms, and casinos.\n\nRatchet is the largest port on Kalimdor, with as many ships bringing cargo in as there are ships heading out for other sites around Kalimdor. In addition to legitimate trade vessels, pirate craft receive amnesty while in the port of Ratchet as long as they can pay the stiff docking fees. This situation makes many merchant captains furious, but they cannot hope to stay in business if they boycott Ratchet. Moreover, the Lawkeepers and hired mercenaries prowling the waterfront are eager to deal with anyone looking to cause trouble.\n\n[h3]Reputation[/h3]\nMost of the quests to raise reputation with Ratchet and the Steamwheedle Cartel are located in the Barrens. Having a friendly or higher reputation will make the guards help you in case of initiated violence against you.\n\nIf you are Hated with Rachet, you can do the repeatable quest [quest=9267] to get back to Neutral.',NULL),(8,471,0,NULL,0,2,'The Wildhammers are a clan of dwarves currently centered in the [zone=47] and [zone=3520]. The faction has been removed in patch 2.0.1.\n\n[h3]History[/h3]\n\nJust prior to the [object=175739], the Wildhammer Clan, ruled by Thane Khardros Wildhammer, inhabited the foothills and crags around the base of Ironforge. The Wildhammer Clan was unsuccessful in wresting control of [zone=1537] from the Bronzebeard and Dark Iron clans. Khardros and his Wildhammer warriors traveled north through the barrier gates of Dun Algaz, and founded their own kingdom within the distant peak of Grim Batol. There, the Wildhammers thrived and rebuilt their stores of treasure.\n\n[npc=9019] and his Dark Irons vowed revenge against Ironforge. Thaurissan and his sorceress wife, Modgud, launched a two-pronged assault against both Ironforge and Grim Batol. As Modgud confronted the enemy warriors, she used her powers to strike fear into their hearts. Shadows moved at her command, and dark things crawled up from the depths of the earth to stalk the Wildhammers in their own halls. Eventually Modgud broke through the gates and laid siege to the fortress itself. The Wildhammers fought desperately, Khardros himself wading through the roiling masses to slay the sorceress queen. With their queen lost, the Dark Irons fled before the fury of the Wildhammers.\n\nOnce the immediate Dark Iron threat was eliminated, the Wildhammers returned home to Grim Batol. However, the death of the Modgud had left an evil stain on the mountain fortress, and the Wildhammers found it uninhabitable. Khardros took his people north towards the lands of Lordaeron. Settling within the mountainous region of the Aerie Peaks and The Hinterlands, and lush forests of Northeron, the Wildhammers crafted the city of Aerie Peak, where the Wildhammers grew closer to nature and even bonded with the mighty gryphons of the area. Over time they started calling their land the Hinterlands. \n\n[b]Modern Wildhammers[/b]\nThe Wildhammer Clan currently makes its home at Aerie Peak in the Hinterlands. The most immediate threat to their security comes from the east in the form of the Witherbark Trolls and Vilebranch Trolls. They are most famous for riding into battle atop Gryphons, while wielding powerful Stormhammers.\nWildhammer dwarves have a number of clans, each ruled by a Thane. The strongest Thane rules Aerie Peak.',NULL),(8,509,0,NULL,0,2,'[b]The League of Arathor[/b] was originally established by the survivors of the Kingdom of Stromgarde to reclaim the [zone=45] from the hands of the Forsaken Defilers in Hammerfall. Today it is an organization in support of the Alliance, based out of the [zone=3358] in Refuge Pointe. They have taken it upon themselves to help supply the Alliance forces where needed, and their members include all manner of Alliance races - even though they are still predominantly Stromgardian humans.\n\n[h3]Reputation[/h3]\nPlayers can earn reputation in this faction by participating in the Arathi Basin battleground. When you fight in Arathi Basin you earn 10 reputation per 160 resources. On Arathi Basin holiday weekends the required resources is reduced to 150.\n\nYou are granted the player title [title=48] once exalted with League of Arathor and the other two battleground factions, [faction=890] and [faction=730].',NULL),(8,510,0,NULL,0,2,'[b]The Defilers[/b] seek to foil the [faction=509] in the [zone=3358] battleground. Today it is an organization in support of the Horde, based out of Hammerfall in [zone=45]. They have taken it upon themselves to help supply the Horde forces where needed, and their members include all manner of Horde races - even though they are still predominantly orcs.\n\n[h3]Reputation[/h3]\nReputation is gained through participation in the Arathi Basin battleground. When you fight in Arathi Basin you earn 10 reputation per 160 resources. On Arathi Basin holiday weekends the required resources is reduced to 150.\n\nYou are granted the player title [title=47] once exalted with the Defilers and the other two battleground factions, [faction=889] and [faction=729].',NULL),(8,529,0,NULL,0,2,'The [b]Argent Dawn[/b] is an organization focused on protecting Azeroth from the threats that seek to destroy it, such as the Burning Legion and the Scourge. Strongholds of the Argent Dawn can be found in the [zone=139] and [zone=28]. It also maintains a presence in [zone=1657] and in the [zone=85], among other less notable areas. Reputation with the Argent Dawn can be used to purchase various profession recipes, misc. consumables, and to mitigate the cost of attunement to [zone=3456]. With the expansion of the Burning Crusade, Argent Dawn reputation has decreased in value.\n\nArgent is Latin for silver, which could explain why the [item=22999] has an icon of a silver sun rising.[h3]History[/h3]After the death of the [npc=16062], the corruption of the Scarlet Crusade became apparent to some of its members, who subsequently left the ranks of the [url=?search=scarlet+crusade#M0z]Scarlet Crusade[/url] and established the Argent Dawn to protect Azeroth from the threat of the Scourge without the blind zealotry present in the Scarlet Crusade.\n\nWhile they share the same goals as the Crusade, the Argent Dawn has opened its ranks to not only other Alliance races besides Humans, but also members of the Horde and even some of the Forsaken. They caution discretion and introspection, and put a lot of emphasis on researching the Scourge and how to combat them.\n\nWith time the Argent Dawn has grown diversified, and like its progenitor — the Scourge — has split again, with an offshoot called the [url=?search=brotherhood+of+the+light]Brotherhood of the Light[/url], a compromise between the Argent Dawn\'s more scholarly approach and the Scarlet Crusade\'s fanaticism.\n\n[h3]Reputation[/h3]\n[b]Scourgestones[/b]\nWhile wearing a trinket granting the Argent Dawn Commission effect, characters can loot [url=?items=12&filter=na=scourgestone]scourgestones[/url] from undead monsters they\'ve killed, and subsequently turn them in in exchange for [item=12844]. These turn-ins require various numbers of [item=12843], [item=12841], and [item=12840]. It should be noted that the token items received from the turn-ins should be saved until after Revered status is reached, as the quest turn-ins will no longer grant reputation after this point.[pad][b]Cauldrons[/b]\nAnother way to gain reputation with the Argent Dawn is through repeatable \"Cauldron\" quests. The Cauldrons are a source of \"undeathness,\" that contribute to the Scourge\'s numbers.[pad][b]Instances[/b]\nLike most factions, the player can run instances to increase his reputation. These instances are [zone=2017] and [zone=2057]. Naturally, these instances also include quests that will raise Argent Dawn reputation, as well as include Scourgestone drops.',NULL),(8,530,0,NULL,0,2,'[b]Darkspear Trolls[/b], the tribe of exiled trolls that has joined forces with [npc=4949] and the Horde. They now call [zone=1637] their home, which they share with their orc allies. [npc=10540] is their current leader.\n\n[h3]History[/h3]\nAs tribal rivalries erupted throughout the former Gurubashi Empire, the Darkspear Tribe found themselves driven from their homeland in [zone=33]. Having settled in what are believed today to be the Broken Isles, the tribe soon found themselves entangled in a conflict with a band of murlocs. Their fate seemed sealed until the orcish Warchief Thrall and his band of newly freed orcs took shelter on their island home. Controlled by a Sea Witch, a group of rampaging murlocs captured the Darkspears\' leader Sen\'jin, along with Thrall and several other orcs and trolls. Thrall managed to free himself and others, but was ultimately unable to save the trolls\' leader. Although Sen\'jin was sacrificed to the Sea Witch, he was able to reveal a vision he had in which Thrall would lead the Darkspear from the island. \n\nAfter returning to the island, Thrall and his followers managed to fend off further attacks by the Sea Witch and her murloc minions, and set sail for Kalimdor once again. Under the new leadership of [npc=10540], the Darkspear swore allegiance to Thrall\'s Horde and followed him to Kalimdor. Now considered enemies by all other trolls except the Revantusk and the Zandalari, the Darkspear are held in contempt to this day. Yet, the Darkspear have not forgotten being driven from their ancestral homes and this animosity is eagerly returned, especially towards the other jungle trolls. Having reached the orc\'s new homeland, [zone=14], the trolls carved out another home for themselves - this time among the Echo Isles on the eastern shores of the new orc kingdom. \n\nHowever, with the coming of Kul Tiras and its navy, the Darkspear were forced to retreat inland under the onslaught of the misguided commander [npc=177201]. The trolls, fighting alongside their horde brethren, defeated the enemy and reclaimed their new homeland. Shortly thereafter, a witch doctor by the name of [npc=3205] began using dark magic to take the minds of his fellow Darkspear. As his army of mindless followers grew, Vol\'jin ordered the free trolls to evacuate, and Zalazane took control of the Echo Isles. The Darkspear have since settled on the nearby shore, naming their new village after their old leader, Sen\'jin. From Sen\'jin Village they, along with their allies, send forces to battle Zalazane and his enslaved army.\n\n[h3]Reputation[/h3]\n[npc=14727] has the repeatable cloth reputation quests. As a reward for being exalted with the Darkspear Trolls, non-troll Horde players are able to ride [url=?items=15.5&filter=na=Raptor;cr=93:92;crs=2:1;crv=0:0]raptors[/url].\n\nSurrounding zone Durotar contain the most quests for gaining reputation with the Darkspear Trolls. As well, higher level players with the Burning Crusade also have a good amount of quests in [zone=3521].',NULL),(8,576,0,NULL,0,2,'As the last uncorrupted furbolg tribe (at least in their view), the [b]Timbermaw[/b] seek to preserve their spiritual ways and end the suffering of their brethren.\n\nThe Timbermaw Furbolgs inhabit two areas: [zone=16] and [zone=361]. They are presumed to be the only furbolg tribe to escape demonic corruption, though this may not be true due to the existence of [npc=3897], an uncorrupted furbolg of unknown tribe, and the Stillpine tribe on [zone=3524] in Burning Crusade. However, many other races kill furbolg blindly now, without bothering to see if they are friend or foe. For this reason, the Timbermaw furbolg trust very few.\n\nAdventurers who seek out Timbermaw Hold in northern Felwood and prove themselves as friends of the Timbermaw will learn that the furbolgs value their friends above all else. Though they possess no fine jewels or any worldly riches, the Timbermaw\'s shamanistic tradition is still strong. They know much about the art of crafting armors from animal hides, and they are more than happy to share their healing/resurrection knowledge with friends of their tribe. Besides, any reputation above Unfriendly will also grant you untroubled access to [zone=493] and [zone=618] through their tunnels.\n\n[h3]Reputation[/h3]\nReputation with the Timbermaw Hold faction is mainly gained through quests and killing in Felwood. The members of the Deadwood Tribe, another Furbolg tribe in Felwood, are the Timbermaws\' main enemies.\n\n[ul]\n[li]Killing one [url=?npcs&filter=na=Winterfall]Winterfall[/url] or [url=?npcs&filter=na=Deadwood]Deadwood[/url] Furbolg gives 10 reputation points. Gains stop at revered; Deadwoods give 2 reputation point at honored.[/li]\n[li]Killing either one of the Deadwood Bosses [npc=9464] or [npc=9462], is worth 60 reputation. There is no reputation limit.[/li]\n[li]Killing the elite Winterfall Furbolg, [npc=10738], located in a cave east of [faction=577], awards 50 reputation. There is no reputation limit, and his respawn rate is 6 to 8 minutes.[/li]\n[li]Killing the named rare mob [npc=14342] is worth 50 reputation. He is a rare spawn at Deadwood Village in Felwood and there is no reputation limit for this mob.[/li]\n[li]Killing the named rare mob [npc=10199] is worth 50 reputation. He is a rare spawn at Winterfall Village in Winterspring. Killing him will grant reputation up until Revered.[/li]\n[li]After completing [quest=8460], turning in 5 [item=21377] yields 150 reputation.[/li]\n[li]After completing [quest=8464], you will be able to turn in [item=21383] collected from furbolgs in Winterspring. Turning in 5 beads at [npc=11556] yields 150 reputation.[/li]\n[/ul]',NULL),(NULL,NULL,0,'commenting-and-you',0,2,'[menu tab=2 path=2,13,0]One of many useful features is the user-submitted comment system. This system allows users to submit their own comments to augment the data provided here. As a rule, we promote the submission of informative comments, but we also like to see the occasional joke. Moderators and users alike will apply positive and negative ratings to comments in an effort to promote the useful ones and purge unnecessary information.\r\n\r\nWith that in mind, below is a guide that can be used to determine how your comment will likely be received by the community. \r\n\r\n[pad]\r\n\r\n[tabs name=comments]\r\n\r\n[tab name=\"Before you post\"]\r\n\r\n[ul]\r\n[li][b]Read existing comments[/b] – Sometimes, the information you have may already have been posted by another user. In this case, if the information is useful, the existing comment should be given a positive rank. Posting information that was already added in a previous comment will likely result in a negative rating.[pad][/li]\r\n[li][b]Verify your facts[/b] – Make sure that what you have to post is true. A friend might tell you that a mob is immune to Frost Nova, but unless you verify that yourself, you could be posting a potentially misleading comment.[pad][/li]\r\n[li][b]Temporary usability[/b] – If you want to correct invalid or missing information on a page, keep in mind that your comment may go from a positive ranking to a negative ranking when the correction occurs. For example, informing the community that a spell is cast by Illidan Stormrage before that data has been collected will be useful at first, but once Aowow learns to parse that information and adds it to the \'Abilities\' tab, your comment becomes redundant. If you do not want to worry about the comment or do not want one of your comments to be rated negatively, consider informing us in the [url=/?forums&board=1.]Site Feedback[/url] forum. The moderation staff will be happy to add a comment to correct invalid or missing information on the page for you. Alternatively, you can delete your comment later when it becomes redundant.[/li]\r\n[/ul]\r\n\r\n[/tab]\r\n\r\n[tab name=\"Comment ratings\"]\r\n\r\n[h3][color=q2]Positive (+1)[/color][/h3]\r\n[ul]\r\n[li][b]Corrections on drop percentages[/b] – There are many instances where drop percentages will be inaccurate. For example, quest items do not drop for people who do not have the quest, so their drop percentages will be low. Also, mobs that periodically do not drop loot when they die won\'t count against the drop percentages, so these mobs may appear to have higher drop rates for some items.[pad][/li]\r\n[li][b]Strategies[/b] – If you have a strategy that can assist other users in completing a quest or defeating a mob, by all means, share![pad][/li]\r\n[li][b]Quest coordinates[/b] – Providing coordinates for the location of quest items or mobs is always useful. When possible, you should provide links to quest targets as well.[pad][/li]\r\n[li][b]Theorycrafting[/b] – We encourage users to post any information they have regarding complex calculations they may have performed to, for example, prove one item has a higher DPS than another given certain abilities.[pad][/li]\r\n[li][b]Just for laughs[/b] – If your comment is one that would be universally funny (i.e. not an inside joke), post away. We like to laugh as much as anyone else. Of course, whether your joke is funny or not is subject to our other users. :)[/li]\r\n[/ul]\r\n\r\n[h3][color=q10]Negative (-1)[/color][/h3]\r\n[ul]\r\n[li][b]Redundant information[/b] – For instance, a comment that says \"Dropped by Ragnaros\" does not add anything to the page as that information can be viewed in the \"Dropped By\" tab of the page in question.[pad][/li]\r\n[li][b]Soloed by:[/b] Unless your comment contains a detailed explanation of how you defeated a mob, these comments do not add anything to the page. Simply stating your level, class, and that you soloed the mob by using a few skills is not enough to be useful.[pad][/li]\r\n[li][b]Dropped in X kills[/b] – Telling users that you were lucky enough to get the crusader enchant in one drop is not considered useful information.[pad][/li]\r\n[li][b]NPC/Object coordinates[/b] – The coordinates for NPC or mobs are already supplied in convenient maps within the interface.[pad][/li]\r\n[li][b]Best X before level Y[/b] – Simply posting that an item is the best twink weapon or the best dagger for a rogue is not helpful unless you can back up that claim with facts.[pad][/li]\r\n[li][b]HUNTAR WEPPON[/b] – While it would be acceptable to explain why you feel a certain class with a certain spec would gain the most benefit from an item, simply stating that you feel the weapon should always go to a hunter in a raid will result in negative moderation.[pad][/li]\r\n[li][b]Confirmed![/b] – Adding a comment that simply indicates that you have confirmed a comment left by someone else clutters the comments. The best way to confirm a comment as correct is to give it a positive ranking. A comment with a high ranking will indicate to users that many people think it is useful data.[/li]\r\n[/ul]\r\n\r\n[/tab]\r\n\r\n[tab name=Deletion]\r\n\r\nAny comment that does not abide by the same [forumrules] will be deleted by a moderator.\r\n\r\n[/tab]\r\n\r\n[/tabs]',NULL),(NULL,NULL,0,'item-comparison',0,2,'[menu tab=2 path=2,13,5]Can\'t find the answer you were looking for? Just [url=/?aboutus#contact]contact us[/url], or post on our [url=/?forums&board=1]forums[/url]! \r\n\r\n[pad]\r\n\r\n[tabs name=compare]\r\n\r\n[tab name=\"General usage\"]\r\n\r\n[h3]Basic Controls[/h3]\r\n\r\n[ul]\r\n[li][img src=STATIC_URL/images/icons/save.gif border=0] [b]Save[/b] – Saves the comparison so that you may continue browsing the site without losing it. When you click on the [b]Compare[/b] button found throughout the site you will be given the option to add to your saved comparison.[/li]\r\n[li][img src=STATIC_URL/images/icons/refresh.gif border=0] [b]Autosaving[/b] – Indicates that you are viewing your saved comparison, and that any changes you make will automatically be saved. To avoid modifying your saved comparison, you may click on Link to this comparison before making any changes.[/li]\r\n[li][img src=STATIC_URL/images/icons/link.gif border=0] [b]Link to this comparison[/b] – Provides a link to a new page with the current item comparison already there! Useful for showing friends your item comparisons.[/li]\r\n[li][img src=STATIC_URL/images/icons/delete.gif border=0] [b]Clear[/b] – Removes all items, groups, and weights from the comparison tool, giving you a clean slate to work with. [b]This will [u]delete[/u] your saved comparison if used while autosaving.[/b][/li]\r\n[li][img src=STATIC_URL/images/icons/add.gif border=0] [b]Weight scale[/b] – Allows you to add one or more weight scales to the item comparison using your own weights or one of our predefined presets. Each weight scale can have its own name. A saved comparison also contains the weight information, allowing you to store custom weight scales for future use.[/li]\r\n[li][img src=STATIC_URL/images/icons/add.gif border=0] [b]Item[/b] – Opens a live search that displays item suggestions as you type the name of an item. Clicking on a suggestion will add that item to your comparison.[/li]\r\n[li][img src=STATIC_URL/images/icons/add.gif border=0] [b]Item set[/b] – Opens a live search that displays item set suggestions as you type the name of an item set. Clicking on a suggestion will add all of the items in that set to your comparison.[/li]\r\n[/ul]\r\n\r\n[h3]Adding Items[/h3]\r\n[div float=right align=right][img src=STATIC_URL/images/help/item-comparison/addingitems.gif]\r\n[small]Some of the ways to add items to a comparison.[/small][/div]The comparison tool is fully integrated with our site and designed to be as convenient as possible to work with. There are many ways to add items to a comparison depending on what part of the site you are on: \r\n[ul][li]Using the [url=/?compare]item comparison tool[/url] itself, you may add items or item sets using the links in the top right corner as described above.[/li]\r\n[li]Viewing an [url=/?item=35137]item[/url] or [url=/?itemset=-17]item set[/url] page, you may click on the red [b]Compare[/b] button near the Quick Facts box.[/li]\r\n[li]Viewing [url=/?items=4.2&filter=sl=8]search results[/url] or [url=/?npc=34077#sells]any page with a list of items[/url], checkboxes are displayed next to items which can be equipped. You may select one or more items and click the [b]Compare[/b] button at the top of the list.[/li][/ul]\r\n\r\n[i]Note: If you have a comparison saved, and you add items to your comparison from elsewhere on the site, you will be given the option to add them to your saved comparison or create a new one. If you don\'t have a saved comparison, a new comparison will automatically be created and saved with the selected items.[/i]\r\n\r\n[h3]Managing Your Items[/h3]\r\n[div float=right align=right][img src=STATIC_URL/images/help/item-comparison/newgroup.gif]\r\n[small]Creating a new group by dragging an item.[/small][/div]\r\n[ul][li][b]Creating a new group[/b] – [u]Drag an item into the empty column[/u] on the right to create a new group containing that item.[/li]\r\n[li][b]Moving[/b] – To move an item or group, click on the item (or the group\'s control bar) and [u]drag it to the desired position[/u].[/li]\r\n[li][b]Copying[/b] – [u]Holding shift while dragging[/u] an item or group will make a copy of it when it is dropped.[/li]\r\n[li][b]Deleting[/b] – Items and groups can be deleted by [u]dragging them out of the row[/u]. Groups may also be deleted by clicking the X on the right side of the group\'s control bar.[/li]\r\n[li][b]Deleting all but one group[/b] – [u]Holding shift while deleting a group[/u] (see above) will cause all other groups to be deleted instead of that one.[/li]\r\n[li][b]Splitting a group[/b] – Groups of 2 or more items can be split by [u]clicking on [b]Split[/b] in the menu dropdown[/u] on the group\'s control bar. This will create a new group for each item in the current group.[/li]\r\n[li][b]Exporting a group[/b] – [u]Clicking on [b]Export[/b] in the menu dropdown[/u] of the group\'s control bar will take you to a new comparison containing only the current group.[/li]\r\n[li][b]Item Enhancements[/b] - To add gems or enchantments to an item, [u]right-click on the item icon at the top[/u], then select the desired option from the menu. The stats will automatically update—including the set bonuses.[/li]\r\n[/ul]\r\n\r\n[/tab]\r\n\r\n[tab name=\"Advanced features\"]\r\n\r\n[h3]Level Adjustments[/h3]\r\nYou can select your desired character level from the dropdown at the top left. When you do, all the statistics that change according to your level (including combat ratings and heirloom item stats) will automatically adjust to the corresponding value for the level you\'ve entered.\r\n\r\n[h3]Gains[/h3]\r\nAt the bottom of the item comparison is a special row called \'Gains\'. The gains row calculates the minimum values of all stats that appear in any group in the item comparison. It then displays the bonuses each row has [b]above[/b] this minimum.\r\n\r\nFor example, the minimum stamina for any group in [url=/?compare=35031;35030;35029;35028;35027]this comparison[/url] is 50. The gains row displays nothing for the items which have 50 stamina, +23 sta for the item with 73 stamina, and +27 sta for the items with 77 stamina.\r\n\r\nBasically, the gains row removes the shared stats between all groups so that you can focus on what each group brings to the table.\r\n\r\n[h3]Focus Group[/h3]\r\n\r\n[screenshot url=STATIC_URL/images/help/item-comparison/focus2.gif thumb=STATIC_URL/images/help/item-comparison/focus.gif float=right]Comparing arena sets of the first four PvP\r\nseasons using a focus group.[/screenshot]Setting a focus group is done by clicking on the eye icon in the group\'s control bar. Selecting a group as your focus will update the display of the item comparison to show the difference in stats between all other groups and the focus group.\r\n\r\nWhen a focus is set, the focus group is highlighted and each other group has numbers that indicate the stats gained or lost in comparison to the focus group.\r\n\r\n[b][color=q2]Positive[/color][/b] numbers indicate that group has a higher total for a given stat than the focus group, while [b][color=q10]negative[/color][/b] numbers indicate that group has a lower total for a given stat than the focus group. \r\n\r\n[h3]Stat Weighting[/h3]\r\nTo add a weight scale to your comparison, click on the [b]Add a weight scale[/b] link in the top right corner. You may select a weight scale from our predefined presets or create one of your own. Each weight scale may be given a name that will appear in the score tooltips to help differentiate the different scores. You may add as many weight scales as you like.\r\n\r\nTo remove a weight scale, click on the [b]X[/b] next to the appropriate score in any group. To toggle between normalized (default), raw, and percent score mode, click on the score in any group.\r\n\r\nUnlike the weighted item search, these weight scales [b]do not[/b] automatically select gems or include socket bonuses in the score at this time.\r\n\r\n[h3]Viewing a Group in 3D[/h3]\r\nClick on [b]View in 3D[/b] in the menu dropdown of the group\'s control bar to display a 3D model of the items and select the race and gender to display them on. Of course, items which do not have models, such as trinkets and rings, will not be displayed.\r\n\r\n[/tab]\r\n\r\n[/tabs]',NULL),(NULL,NULL,0,'stat-weighting',0,2,'[menu tab=2 path=2,13,3]Can\'t find the answer you were looking for? Just [url=/?aboutus#contact]contact us[/url], or post on our [url=/?forums&board=1]forums[/url]! \r\n\r\n[pad]\r\n\r\n[tabs name=weights]\r\n\r\n[tab name=FAQ]\r\n\r\n[h3]How do weights work?[/h3]\r\nThe weighting system allows you to give a weight value to attributes that matter to you and applies your ratings to items in your search results. Each weight value is multiplied by an item\'s stat points and then added together to get the item\'s total score. This score is used to sort the results and display the highest scoring items.\r\n\r\nIf you decide that spell damage is worth twice as much as spell crit, you could add the weights as 2 and 1, 100 and 50, or any other numbers with the same ratio.\r\n\r\nPlease note that weights only work for [url=/?items=4]Armor[/url], [url=/?items=2]Weapons[/url], [url=/?items=3]Gems[/url] and [url=/?items=0]Consumables[/url]. \r\n[h3]What is the difference between weights and equivalency?[/h3]\r\nThe equivalency of two attributes describes how much one equals the other. You may find equivalency ratings that say something like 1 agility = 1.5 strength. This is [b]not[/b] the same as weight values; in fact, it\'s the exact opposite! Equivalency describes the ratio of the stats to each other, which can be used to derive the stat weights. In this example, an appropriate set of weights might be agility 3 and strength 2; this works out to agility being [i]1.5 times as valuable[/i] as strength. \r\n[h3]Is there a way to save a template that I have created?[/h3]\r\nThere sure is! You can save your stat weighting scales by going to the \'Preset\' dropdown menu, selecting \'custom,\' and then filling in your own weights. After you\'ve modified them to your liking, you can hit \'Save\' to give them a name so they can be used for future searches as well.\r\n\r\nWeights also carry over from one item list to another if you use the database menu, so going from a [url=/?items=2&filter=wt=51:48:49;wtv=83:67:58]weighted list of weapons[/url] to the [url=/?items=4&filter=wt=51:48:49;wtv=83:67:58]cloth armor listing[/url] will also maintain your current weight scale. \r\n[h3]Is it better to match sockets and gain the socket bonus, or use the best gems?[/h3]\r\nThe weighting system answers this for you automatically. It compares the score of matching gems plus the score of the socket bonus, to the score of the best gems it could put in that item. It will automatically put in the gems that result in the highest net rating, taking socket bonuses into account. When the socket colors are matched, the socket bonus text will be listed below the gems for each item. \r\n\r\n[h3]What are the default weight presets based on?[/h3]\r\nWe\'ve done a great deal of research, tracking down equivalence points for all of the classes. We\'d like to thank all of the hard-working theorycrafters at [url=http://elitistjerks.com/f47/t21302-theorycrafting_think_tank/]Elitist Jerks[/url], [url=http://forums.tkasomething.com/showthread.php?t=9542]TKA Something[/url], [url=http://shadowpanther.net/aep.htm]Shadow Panther[/url], [url=http://druid.wikispaces.com/Healing+Gear+List]The Druid Wiki[/url], [url=http://www.emmerald.net/]Emmerald[/url], [url=http://www.lootrank.com/wow/templates.asp]Lootrank[/url], [url=http://pawnmod.trenchrats.com/index.php]Pawn Mod[/url], and [url=http://www.codeplex.com/Rawr]Rawr[/url], as well as a host of threads on the World of Warcraft forums. They provided the inspiration for the weighted search and a starting point for our preset values.\r\n\r\n[/tab]\r\n\r\n[tab name=\"Helpful tips\"]\r\n\r\n[ul]\r\n[li]You can help us [b]improve[/b] our presets! Email your suggestions to [feedback].[/li]\r\n[li]Don\'t weight stats that your character is [b]already capped on[/b] (e.g. Hit rating). Be sure to tweak the presets as needed![/li]\r\n[li]You can adjust a preset by clicking on the \'show details\' button.[/li]\r\n[li]Once you have generated a weighting you like, you can bookmark that page. Then, if you browse around on other pages using the menus at the top, your weight scale will be applied to that page as well.[/li]\r\n[/ul]\r\n\r\n[/tab]\r\n\r\n[tab name=Why?]\r\n\r\n[h3]Why does it give a higher score to 2H weapons over 1H weapons, when using a 1H + OH is better?[/h3]\r\nThe scores are based off the stat weights of the item by itself. Two-handers rank higher because by themselves they do have better stats than a one-hander with nothing else in the off hand. If you add up the scores of a main hand and off hand item, the total score is what you should use to compare to that of a two-hander. We do not assume a score for your offhand item, as there is no way of knowing what you have or can obtain for that slot unless you do a weighted search for it. \r\n[h3]Why does the preset list X as more important than Y?[/h3]\r\nSome attributes come in unusual value ranges on items, which affects their equivalency to other stats. It does not mean that your should focus on or ignore that stat, but that a single point of it is worth more or less compared to other stats. Stats with high number ranges (armor, weapon damage, penetration, etc) will require smaller weight values, while stats with low number ranges (mana regeneration) will require much larger weight values.\r\n\r\nIn essence, giving mana regeneration a score of 100 and healing a score of 25 does [b]not[/b] say that mana regeneration is more important than healing, simply that each point of mana regeneration is the equivalent of 4 points of healing.\r\n[h3]Why don\'t you have a preset for PvP/Tier 6 Raiding/...? Why doesn\'t your preset give a stat value for X?[/h3]\r\nIf you would like to suggest changes to the existing presets or new presets for other specs or situations, please do so to [feedback]. \r\n[h3]Why doesn\'t the preset limit the items to X, Y, and Z?[/h3]\r\nThe weight presets are for sorting; filters are for limiting the search results. If you want to restrict the items you see, use the appropriate tool - the filter options. The only limit applied by the weight scales is that it will not display items with a score of 0 or less. You should continue to use the existing filtering system if you want to see items of a specific type, slot, source, speed, etc.\r\n[h3]Why does it suggest the gems it does for the sockets?[/h3]\r\nThe suggested gems are based on your weights. If you would like to see a different gem in the sockets, try increasing the weight of the appropriate stat. If you feel the weights in the presets need to be adjusted, please let us know at [feedback].\r\n\r\n[/tab]\r\n\r\n[/tabs]',NULL),(NULL,NULL,0,'screenshots-tips-tricks',0,2,'[menu tab=2 path=2,13,2]\r\n\r\nWe thrive on user contributions! Quest data, database comments, forum posts - you name it, we love it! One of our favorite methods of contribution is via uploaded [b]screenshots[/b], images depicting various items, NPCs or quest details in the World of Warcraft. Users can submit screenshots to any database page which will then be reviewed by our staff and, upon approval, added to a database page! Taking and uploading screenshots is easy!\r\n\r\n[small]The information below is graciously provided by [url=http://us.blizzard.com/support/article.xml?locale=en_US&articleId=21048]Blizzard Support[/url].[/small]\r\n[h3]Taking Screenshots on Windows[/h3]\r\n[ul]\r\n[li]While in the game, press the Print Screen key on your keyboard.[/li]\r\n[li]You should see a \"Screen Captured\" message.[/li]\r\n[li]The screenshot will appear as a .JPG file in the Screenshots folder, in your main World of Warcraft directory.[/li]\r\n[li]You should be able to double click on the screenshot files to view the screenshots in Windows default image viewer.[/li]\r\n[/ul]\r\n\r\n[b]Extra notes for Windows Vista users[/b]\r\n[ul]\r\n[li]Due to extra security on the system the screenshots will be saved to the following folder:C:\\\\users\\\\*your user name*\\\\AppData\\\\Local\\\\VirtualStore\\\\Program Files\\\\World of Warcraft\\\\Screenshots[/li]\r\n[li]You may also have to turn on the ability to view hidden files as the AppData folder may be hidden.\r\n[ul]\r\n[li]Click the Start/Window button, select Control Panel, Appearance and Personalization, Folder Options.[/li]\r\n[li]Next click on the View tab, under the Advanced settings, click Show hidden files and folders, and click OK to finish.[/li]\r\n[/ul][/li]\r\n[/ul]\r\n\r\n[h3]Taking Screenshots on Mac[/h3]\r\n[ul]\r\n[li]Players can take a screenshot in-game using the keyboard key bound to the Print Screen functionality.[/li]\r\n[li]If you have a keyboard with an F13 key, press the key to take an in-game screenshot. Players without an F13 key on the keyboard can change the default Screen Shot key in the Key Bindings menu.[/li]\r\n[li]You should see a \"Screen Captured\" message.[/li]\r\n[li]The screenshot will appear as a JPEG file in the Screenshots folder, in your main World of Warcraft folder.[/li]\r\n[/ul]\r\n\r\nRemember to turn off your in-game UI using the Alt+Z (or ⌘+V) command! Upon taking your screenshot, you can then go in and use an image editor (such as the free program [url=http://www.getpaint.net]Paint.NET[/url]) to crop your image for faster upload. You can select specific sections of a screenshot to upload (if you are featuring a particular piece of armor, for example) and save the file, then simply upload your pre-cropped image directly! If not, you can easily crop your screenshot after uploading but before submitting using our handy tool.\r\n\r\nTo submit a screenshot, simply navigate to the database entry for which you\'ve taken a screenshot and navigate to the \'Contribute\' section. Select the \'Submit a screenshot\' tab and click \'Choose file\' to locate the file on your system. Remember that only PNG and JPG file types are accepted! Once you have selected the screenshot simply click \"Submit\" and you\'re on your way! You will then be able to crop the image if necessary before your image is finally submitted for review. Upon approval (which may take up to 72 hours) your screenshot will then be featured on the database page, as well as in a \'Screenshots\' tab in your user profile!\r\n\r\n\r\n[h2]Quality Tips[/h2]\r\n\r\n[screenshot url=STATIC_URL/images/help/screenshots/hinterlands.jpg thumb=STATIC_URL/images/help/screenshots/hinterlands2.jpg float=right]The Hinterlands[/screenshot]A good screenshot is like a miniature piece of art. It should showcase the main object, but take into account the details around it. The same 7 elements of art design come into play here, Line, Shape, Form, Space, Texture, Light & Color. We\'ll touch on several of these and how to make use of the in game settings and mechanics to enhance your pictures.\r\n\r\nTurn your resolution and color sampling as high as your computer can handle. Turn on all the image effects and details, but turn down the weather effects to the lowest setting. In general you want all your glow and spell effects maxed to really show the environment to its fullest potential (they actually help with the lighting too!) You may find a shot that you need to play with these settings to enhance, sometimes turning down environmental detail is helpful to remove extra grasses.\r\n\r\nWorld of Warcraft actually has an internal setting for screenshot quality, and by default that quality is set to [b]3/10[/b]. You can turn this up, though, in order to take higher quality screenshots. In order to do so, type this command into your chatbox:\r\n\r\n[code]/console screenshotQuality 10[/code]\r\n\r\nMost of the time taking the pictures from 1st person view works best, so zoom all the way in so that you\'re looking through your character\'s eyes. Occasionally the object might be too big (large NPCs especially) to use this view - if this is the case get as close to them as you can without having your body in the shot and swing the camera around to get the angle that you\'re looking for.\r\n\r\nPay attention to the light - a well lit picture is 10 times better than a dark one. You may even want to do a little color correcting before uploading - increase the brightness and contrast a touch. For instance - it\'s a lot easier to take pictures in sunny Stormwind than deep in the mountains of torch lit Ironforge. Daytime pictures also turn out better than night.\r\n\r\n[h3]Featuring Armor[/h3]\r\n\r\n[screenshot url=STATIC_URL/images/help/screenshots/armor.jpg thumb=STATIC_URL/images/help/screenshots/armor2.jpg float=right]Dreamwalker Spaulders[/screenshot]We want to see the armor! Not Joe Schmoe in the armor. In general you want close ups of the piece itself (except for full set pictures). Don\'t be afraid to submit a 4 inch picture of one glove. Once\'s it\'s cropped and loaded and shrunk down to the thumbnail it will look great!\r\n\r\nUse your best judgment when cropping armor pics, but remember - we want to see details of the armor - not the person or a far away image. Of course, this also applies to weapons or any other piece of equipment!\r\n\r\n[h3]Featuring NPCs[/h3]\r\n\r\n[screenshot url=STATIC_URL/images/help/screenshots/npc.jpg thumb=STATIC_URL/images/help/screenshots/npc2.jpg float=right]Cairne Bloodhoof [/screenshot]Full body shots should be the norm. If you can\'t get a good full shot (e.g. they\'re standing behind a counter) get the waist up shot. There\'s no need to include the on-screen text and titles of NPCs. The website already lists those, so just get in close and take a great shot of the NPC itself.\r\n\r\nGet down on their level - you may need to \"/sit\" or even \"/sleep\" to get a good view of something low to the ground (scorpions, boots, spiders, etc.)\r\n\r\nWhen capturing moving NPCs, try to get as much a head on front shot as you can, being willing to take a few hits while you take picture of a mob attacking you can make for a great shot. If you don\'t want to get your hands dirty, sitting in place for a while and waiting for it to path in front of you is often easier and faster than running around it trying to get your shot.\r\n\r\nTalking to friendly NPCs will usually make them face you - you can then spin around and get the best background for your picture. You may also catch them in an interesting motion or gesture.',NULL),(NULL,NULL,0,'profiler',0,2,'[menu tab=2 path=2,13,6]Can\'t find the answer you were looking for? Just [url=/?aboutus#contact]contact us[/url], or post on our [url=/?forums&board=1]forums[/url]!\r\n\r\n[pad]\r\n\r\n[tabs name=profiler]\r\n\r\n[tab name=\"Browsing characters\"]\r\n\r\n[div float=right align=right][img src=STATIC_URL/images/help/profiler/menu.gif]\r\n[small]Navigating the menu to your battlegroup and realm.[/small][/div]We maintain a database of [i]millions[/i] of [url=http://www.wowarmory.com/]Armory[/url] characters, guilds, and arena teams that have been imported by our users. You can browse through this extensive list by visiting the main [url=/?profiles]profiles[/url] page and selecting a region, battlegroup, or realm from the menus at the top.\r\n\r\nThis will give you an unfiltered look at the players and guilds in the area you selected, with the most recently updated characters displayed first. You can also enter your characters name in the box at the top to jump directly to that character.\r\n\r\n[h3]Finding My Characters[/h3]\r\n\r\n[ul]\r\n[li]Use the breadcrumb listings at the top to browse to your region, battlegroup, and realm. When you do this, a box will appear in the listing at the top of the page. Enter your character\'s name in this box to be taken directly to your character. You can use the \"Claim Character\", which is located under the Manage Character button, to save a character to your [url=/user=fewyn#characters]user page[/url] for later viewing.[/li]\r\n[/ul]\r\n\r\n[i]Tip: Claimed characters can be made public or private as you choose—so you only show off the characters people want you to see! Basic information for the profiles will remain public, just as it is in the Armory—but any connection to your account will be hidden.[/i]\r\n\r\n[h3]Filters[/h3]\r\nBut that\'s not the only way to find a character! You can also search Profiles using our robust filter system, just the same way that you can search items, NPCs, or spells in game. Characters and guilds can be filtered by name, region, and realm to limit the number of displayed results.\r\n\r\nAdditionally, characters can be filtered by faction, level, race, and class – as well as a number of other unique and useful criteria. For example:\r\n\r\n[ul]\r\n[li][div float=right align=right][img src=STATIC_URL/images/help/profiler/filters.gif]\r\n[small]Searching for characters that match your criteria.[/small][/div]Let\'s see [url=/?profiles=us.draenor&filter=cl=8;ra=11;cr=35;crs=0;crv=450]all the Draenei mages on my server that have their tailoring maxed out[/url].[/li]\r\n[li]Hmm... I wonder if anyone is [url=/?profiles=eu&filter=na=Malgayne]using my name on European servers[/url]?[/li]\r\n[li]How do I compare to [url=/?profiles=us.draenor&filter=cl=2;minle=80;maxle=80;cr=7;crs=1;crv=50]other Retribution-specced paladins on my server[/url]?[/li]\r\n[li]How many [url=/?profiles&filter=cr=23;crs=0;crv=871]Bloodsail Admirals[/url] are there out there?[/li]\r\n[li]Who got caught wearing a [url=/?profiles&filter=cr=21;crs=0;crv=22279]Lovely Black Dress[/url]?[/li]\r\n[li]How many people on my server and faction [url=/?profiles=us.sentinels&filter=si=2;cr=23;crs=0;crv=2904]completed Heroic Ulduar[/url]?[/li]\r\n[/ul]\r\n\r\nWe\'ll be adding more filters as time goes on, so feel free to experiment – and let us know if you think of other ideas!\r\n\r\n[pad][pad][pad]\r\n\r\n[h3]Guild and Arena Team Rosters[/h3]\r\nWhen you click on a character\'s guild or arena team, you will be directed to a roster view listing all the characters that belong to it. The roster view displays additional information, including guild ranks and personal arena team ratings. You can further filter this information using the [b]Create a filter[/b] link, should you want to find characters matching specific criteria. Now its easy to find all of the crafters in your guild!\r\n\r\n[h3][img src=STATIC_URL/images/help/profiler/queue.gif float=right]Resync Queue[/h3]\r\nWhen a character resync is requested, it is added to the queue. The queue is used to make sure everyone\'s characters are updated and processed in the order they were submitted, without overloading the [url=http://us.battle.net/wow/en/]Battle.net Armory\'s API[/url] with requests. Whenever you access a character that does not exist in our database or has not been updated in more than 1 hour, it will automatically be added to the queue.\r\n\r\n[/tab]\r\n\r\n[tab name=\"General usage\"]\r\n\r\nThe profiler has a wealth of information it can display about characters and custom profiles, so it can seem daunting at first! Each of the sections are broken down in detail below.\r\n[h3]Basic Profile Information[/h3]\r\nAt the top of a profile you will see an expanded header with vital information about the profile itself. All profiles have an icon and the character\'s race, class and level; Armory characters display a link to the character\'s guild under the name, while custom profiles display a description set by the user that created it. A link to [b]Edit[/b] this information appears on the bottom line, allowing you to update a profile you created or make a new custom profile from an existing one.\r\n\r\n[ul]\r\n[li][img src=STATIC_URL/images/help/profiler/edit.gif float=right][b]Name [/b]– Give your profile a name! Names must start with a letter, and can only contain letters, numbers, and spaces.[/li]\r\n[li][b]Level[/b] – Select a level for your profile. Profiles must be at least level 10 (55 for Death Knights) and no more than level 85.[/li]\r\n[li][b]Race[/b] – Ever wonder what you\'d look like as a tauren instead of an orc? Choose any race for your profile, and the character model with automatically be updated.[/li]\r\n[li][b]Class[/b] – You can select any class you like, regardless of racial restrictions. See what your stats would be if you were a draenei druid![/li]\r\n[li][b]Gender[/b] – Select male or female to set your character\'s gender.[/li]\r\n[li][b]Icon[/b] – Icons are automatically generated for Armory characters and in game class/race combinations, but you can change the icon to any you like.[/li]\r\n[li][b]Description[/b] – Enter a tag line or brief description for the profile so you and others know what it is about.[/li]\r\n[li][b]Visibility[/b] – Public profiles will be visible on your user page and anyone can view a public profile. Private ones will not be displayed or visible to others.[/li]\r\n[/ul]\r\n[i]Note: If you edit a character in any way, it will become a custom profile. The reputations, achievements, and raid progress information will be removed.[/i]\r\n\r\n[h3]Managing Profiles[/h3]\r\nIn the upper right are a number of useful buttons for managing profiles without having to go back to your user page. Each of the buttons have several options that can be used to manage the character\'s page you are currently on and include the following options.\r\n\r\n[ul]\r\n[li][b]Custom Profile[/b]\r\n[ul][li][b]New[/b] – This is a quick link to creating a new, blank profile from scratch. It will open in a new window so you do not lose your current profile. This option is always available.[/li]\r\n[li][b]Save[/b] – Save any changes you have made to this profile. This option is only available for logged in users on profiles they own.[/li]\r\n[li][b]Save as[/b] – This will let you save your current changes under a new name. It is extremely useful for making copies of profiles! This option is only available for logged in users.[/li][/ul][/li]\r\n[li][b]Manage Character[/b]\r\n[ul][li][b]Resync[/b] – Request that the character be updated from the armory; it will be added to the queue. This option is only available on Armory character pages.[/li]\r\n[li][b]Claim character[/b] – Adds an Armory character to your user page. This is a good thing to do with all your alts. This option is only available for logged in users on Armory character pages.[/li]\r\n[li][b]Remove[/b] - Removes the character from your user page. Use this if you no longer play the character or have long since deleted it.[/li]\r\n[li][b]Pin/Unpin[/b] - Pin one of your characters so you can perform personalized searches throughout the database for missing or completed quests, achievements, recipes and more![/li]\r\n[/ul][/li]\r\n[/ul]\r\n\r\n[h3]From the User Page[/h3]\r\n[img src=STATIC_URL/images/help/profiler/userpage.gif float=right]All of your claimed Armory characters and custom profiles are listed in one convenient place on your user page. From the [b]Characters[/b] tab you can remove one or more claimed characters. The [b]Profiles[/b] tab allows you to create a new profile, delete profiles, or change the visibility settings of profiles. Your private profiles will not be visible to anyone else.\r\n\r\n[i]Tip: When you are logged in, all of your characters and custom profiles can be accessed from the [b]My profiles[/b] menu at the top right of any page![/i][pad]\r\n[h3]Saving Your Work[/h3]\r\nAny profile can be edited, even if you don\'t own it, but you\'ll probably want to save your work when you\'re done! You must have an account with us in order to save a profile. Once you\'ve created an account, you can bookmark any number of Armory characters and save up to 10 custom profiles. Premium users will be able to create even more, so upgrade if 10 just isn\'t enough! You can use the red buttons to save a profile from its page, and manage your existing profiles and characters from your user page. \r\n\r\n[/tab]\r\n\r\n[tab name=\"Inventory and talents\"]\r\n[img src=STATIC_URL/images/help/profiler/character.jpg height=300 float=right]The main tab for a profile is the character inventory, which includes a lot of the same information you would see by looking at your character pane in game. This tab is broken up into four key sections - the character view, quick facts box, statistics, and gear summary.\r\n\r\n[h3]Character View[/h3]\r\nThe first thing you\'ll notice, of course, is your character – as rendered by our custom built modelviewer, in all it\'s three-dimensional glory. You can turn the character with your mouse, and zoom in and out using the A and Z keys, just like the modelviewer elsewhere in the site. [b]We even pull your face, hair, and skin color information from the Armory![/b]\r\n\r\nOn either side of the character are inventory icons which you can right click on for a menu of options:\r\n\r\n[i]Tip: You can remove a gem or enchant by clicking None in the picker window or by right clicking on it in the gear summary.[/i]\r\n\r\n[ul]\r\n[li][img src=STATIC_URL/images/help/profiler/itemmenu.gif float=right][b]Equip... / Replace...[/b] – Selecting this option will give you a quick search box in which you can type an item\'s name. Click on the item or hit return to equip it.\r\nUnequip – Unequips the item, of course. :)[/li]\r\n[li][b]Add / Replace enchant...[/b] – The spell icon on the left shows if the item is enchanted. This opens a customized picker window with all enchants available for the item slot.[/li]\r\n[li][b]Add / Replace gem...[/b] – The icon on the left shows the socket color or socketed gem. Like the enchants, this opens a picker window with valid gems for the socket.[/li]\r\n[li][b]Extra socket[/b] – The check mark on the left indicates if a blacksmithing socket has been added to this item. Click to toggle on or off.[/li]\r\n[li][b]Clear Enhancements[/b] - This will remove all reforges, enchantments, gems and extra sockets from an item. Useful if you want to start fresh with an item.[/li]\r\n[li][b]Display on character[/b] – The checkmark on the left indicates if the item is displayed on the model. Click to toggle on or off – it works for more than just cloaks and helms![/li]\r\n[li][b]Compare[/b] – Adds the item to the [url=/?compare]item comparison tool[/url] and opens it in a new window to compare with other items.[/li]\r\n[li][b]Find upgrades[/b] – Uses our [url=/?help=stat-weighting]weighted search[/url] to find upgrades based on your talent spec.[/li]\r\n[li][b]Who wears this?[/b] – Creates a filtered list of other Armory characters who are also wearing the item.[/li]\r\n[/ul]\r\n\r\n[i]Tip: Items that can take enchantments but have no enchantment, or which have empty sockets, will even have a little notification in the tooltip![/i]\r\n\r\n[img src=STATIC_URL/images/help/profiler/quickfacts.gif float=right][h3]Quick Facts Box[/h3]\r\nOn the right hand side is a handy Quick Facts box that displays basic, defining information about a profile. This box is chock full of useful information, including talent spec, achievement points, and professions.\r\n\r\n[i]Tip: Any raid icon that\'s ringed in [color=c4]gold[/color] is a raid that the character has cleared![/i]\r\n[h3]Statistics[/h3]\r\nYou\'ll also notice that all of a profile\'s statistics are laid out beneath the character view. This is also all information you can get from the Armory (and then some), but we lay it out in a nice, convenient page so you can view it all at once – no more messing with drop down menus. You can also click on a statistic and expand it so you can see its tooltip information right there on the page—or click on the header to expand all the related statistics. Your statistics are updated as you edit any part of a profile, including race, class, level, items, enhancements, or talents – all in real time! [b]Statistic modifications from glyphs and buffs are not presently supported, but will be in the future.[/b]\r\n\r\n[i]Note: These statistics are calculated manually – they are not pulled from the Armory. Statistics calculations are still in beta and will ironed out as we go.[/i]\r\n\r\n[img src=STATIC_URL/images/help/profiler/statistics.gif float=center]\r\n\r\n[h3]Gear Summary[/h3]\r\n[div float=right align=right][img src=STATIC_URL/images/help/profiler/gearsummary.gif]\r\n[small]A warning message is displayed for missing enhancements.[/small][/div]Last on the character inventory tab, but not least, is the gear summary. This is a personalized list of all items worn by the character, with convenient column headers and in line filtering options. Use it to see where most of a character\'s items come from, what is the best and worst piece, and whether or not there are missing gems and enchants. Just in case the empty icons aren\'t clear enough, a warning appears at the top of the list if a character is missing gems, enchants, or blacksmith sockets. This [color=q10]warning[/color] is based on the professions of the character if it is an Armory profile, and otherwise shows you everything missing on custom profiles.\r\n\r\nThe gems and enchants can also be edited from within the gear summary, and have a few additional options not available in the character view. You can remove or replace an enhancement from here, and you can find upgrades using our [url=/?help=stat-weighting]weighted search[/url] – just like items!\r\n\r\n[h3]Talents[/h3]\r\nThe talents tab includes an inline version of our [url=/?talent]talent calculator[/url] with a full display of a character\'s talents. It is locked by default, but you can unlock it to begin editing talents, just as you would normally. There are two extra features in the Profiler\'s talent calculator: you can store and swap between two specs for each character, and export the current talent build to the calculator to link to your friends. When you change your talents (or swap between specs) your gear score and statistics will be updates real time!\r\n\r\n[/tab]\r\n\r\n[tab name=\"Other tabs\"]\r\n\r\n[h3]Reputation[/h3]\r\nThe reputation tab displays the complete faction information of an Armory character, with collapsible headers for each section. Its much easier to read than the tiny faction pane in game! Of course, you can link directly to the faction\'s page to get more information about that faction. \r\n[h3][img src=STATIC_URL/images/help/profiler/achievements.gif float=right]Achievements[/h3]\r\nThe achievements tab lists an Armory character\'s progress in each of the main achievement categories, and has a filterable list of achievements including date completed. All of the normal column and list filters are available, along with some new ones! You can filter the list by earned, in progress or complete achievements – complete are displayed by default – or click on any of the category progress bars to only display achievements from that category.\r\n\r\n[/tab]\r\n\r\n[tab name=Completion_Tracker]\r\n\r\n[img src=STATIC_URL/images/help/profiler/quests.jpg float=right width=450]You can use the Profiler\'s [b]Completion Tracker[/b] feature to keep track of your quests, achievements, pets, mounts, recipes, and more!\r\n\r\n[h3]Getting Started[/h3]\r\n\r\nIn order to start tracking your completion data, all you need to do is visit your character\'s page on the profiler and resync it. This will automatically collect data about your character\'s completed achievements, companion pets, mounts, quests, recipes, reputations and titles.\r\n\r\n[h3][img src=STATIC_URL/images/help/profiler/completion.jpg float=right]Tracking Your Completion Data[/h3]\r\n\r\nOnce you\'ve got your data up on the site, it will be available in the form of five new tabs: [b]mounts[/b], [b]companions[/b], [b]recipes[/b], [b]quests[/b], and [b]titles[/b].\r\n\r\nIf you open the mounts, companions, or titles tabs, you\'ll immediately be greeted by a list of all the entries you\'ve already completed. You can cycle through the different tabs to see the ones you already have, the ones you still have yet to collect, a complete list, or a list of just the ones you\'ve \"excluded\" (more on that shortly). You can also use the \"Search within results\" box to search the list based on a keyword, just like you can with other search results in the database.\r\n\r\nThe recipe, and quest tabs, like the Achievements tab, contain more entries—so you\'ll be presented with a box like the one shown above. From there, all you have to do is click one of the progress bars to see the complete tabbed list in each category.\r\n\r\n[h3]Exclusions[/h3]\r\n\r\nWhen you\'re trying to make sure we check off every quest, achievement, or mount on our list, everyone knows that there are some that you just don\'t want to bother with. To that end, we\'ve created [b]exclusions[/b].\r\n\r\n[img src=STATIC_URL/images/help/profiler/exclusions.jpg float=right]Using exclusions, you can flag certain quests, mounts, achievements, recipes, pets, or titles that \"don\'t count\" toward your completion total. When you exclude (for example) a quest, that quest no longer appears in \"incomplete\" listings, and the total number of quests in that category is reduced by one.\r\n\r\n[b]For example:[/b] There are 632 quests in the \"Eastern Kingdoms\" category. If I were to decide that [quest=367] is for noobs and I don\'t want to count it, then all I have to do is put a check in the box next to the quest and click \"Exclude\". After I do so, the Eastern Kingdoms progress bar will only show [i]631[/i] quests total—the remaining quest will appear in the \"Excluded\" tab but won\'t be counted for anything else.\r\n\r\nIf you want to re-include a quest, just go to the \"Excluded\" tab and then use the checkboxes to restore as many as you like. You can do the same thing for achievements, titles, mounts, pets, or recipes.\r\n\r\nIf you [b]complete[/b] a quest that you have excluded, it will show in the progress bar as a [b]+1[/b]. Example: If there are 31 quests in the \"Miscellaneous\" category, and I\'ve completed 20 quests and excluded 1, the progress bar will show [b]20/30[/b]. If I have completed [i]the quest that I excluded[/i], then the progress bar will show [b]20(+1)/30[/b]. If I then go on to complete ALL the quests in that category (including the one I excluded), the progress bar will show [b]30(+1)/30[/b].\r\n\r\n[b]Exclusion Manager[/b]\r\nThe companions and mounts tabs let you manage your exclusions en masse with the Exclusion Manager. Just click the \"Manage Exclusions\" button on top of the tabs to see a list of convenient categories you might want to exclude. There\'s also a \"reset all\" button here to let you wipe all of your exclusions and start over.\r\n\r\n[b]Note:[/b] The Exclusion Manager is currently only available for companions and mounts.\r\n\r\n[i]Tip: Exclusions are tied to your account, not to a particular character. This is so even when you look at someone else\'s character, you\'re judging them by [/i]your[i] completion standards, not anyone else\'s![/i] \r\n\r\n[/tab]\r\n\r\n[tab name=Calculations]\r\n\r\nMost of the information we display is pretty straightforward. A lot of it, particularly the stats on items, is readily available in our database and on various tooltips. There are some new numbers on profile pages that you may ask, what does this number mean? How was it calculated?\r\n[h3]Base Statistics[/h3]\r\nA character\'s five base statistics are determined primarily by his or her class and level. This base amount has a modifier applied to it depending on the character\'s race. We gathered an extensive amount of data from the armory to come up with these base numbers, using untalented individuals of every race, class, and level combination. Because racial modifiers are consistent, we are able to create statistics for \"fake\" race and class combos using the data we already know. However, the Armory does not give data on characters below level 10 or Death Knights below level 55, so we have no statistic information for these profiles. To simplify things, we have set a minimum level for custom profiles based on the available statistics.\r\n[h3]Gear Score[/h3]\r\nOkay, so a lot of sites have gear scores. Most of them (ours included) are based around the [url=http://www.wowwiki.com/Item_level]item budget[/url] Blizzard uses to determine how much of each stat can be on an item. This budget is calculated using the item\'s level, quality, and slot, and we use the budget as the item\'s gear score. You can view a complete breakdown of an item\'s gear score by mousing over it in the [url=/?help=profiler#profiler-inventory-and-talents]gear summary[/url] at the bottom of the character tab. You can view a breakdown of a profile\'s total gear score by mousing over it in the Quick Facts box, also on the character tab.\r\n\r\nEach gear score is color coded based on the item levels of the gear in reference to the character level. [b][color=q0]Grey[/color][/b] for poor, [b][color=q1]White[/color][/b] for common, [b][color=q2]Green[/color][/b] for uncommon, [b][color=q3]Blue[/color][/b] for rare, [b][color=q4]Purple[/color][/b] for epic and [b][color=q5]Orange[/color][/b] for legendary. For example, a level 70 character wearing high item-level, raiding epics from [zone=3606] and [zone=3959] will have a purple-colored gearscore, as their items are considerably \"epic\" quality for their level. However, the same character at 80, if wearing this same gear, will have the gearscore colored blue as the items are of lower-than-optimal quality for their level.\r\n\r\nThe value of an empty socket was generated using the gear score of appropriate gems for the item in question, and subtracted from the item\'s score. This allows us to score unsocketed items lower than an item without sockets of the same level, quality, and slot. Items with better than expected gems will receive higher scores, and items with lower quality gems (or no gems at all) will receive lower scores.\r\n\r\nThe values of enchants are based off of the level of the enchantment. Endgame enchantments are 20 points, profession perks are 40 points, etc. The numbers go down from there.\r\n\r\nYou may notice that some profiles have different gear scores for the same item. There is an extreme difference in budget between a two-handed or one-handed weapon, which causes a discrepancy in scores between characters who should be fairly equal according to the level of their gear. To address this, the gear score of weapons has been normalized so that a character with appropriate weapon choices has the equivalent score of two two-handed weapons. Appropriate weapons are determined by your class and spec; for example, an enhancement shaman should dual wield one handed weapons, a protection warrior should have a one-hander and shield, etc. For classes which the melee weapons don\'t really matter – like hunters or spellcasters – anything they can use is considered appropriate.\r\n\r\n[i]Note: Gear score does not take into account the stats of the item. It is a measurement of quality of gear, not whether the stats on the gear are suited to the character\'s spec.[/i]\r\n\r\n[h3]Guild Scores[/h3]\r\nGuild gear scores and achievement points are derived using a weighted average of all of the known characters in that guild. Guilds with at least 25 level 80 players receive full benefit of the top 25 characters\' gear scores, while guilds with at least 10 level 80 characters receive a slight penalty, at least 1 level 80 a moderate penalty, and no level 80 characters a severe penalty. This is to prevent small guilds and bank alts from appearing to have higher scores than legitimate raiding guilds. Instead of being based on level, achievement point averages are based around 1,500 points, but the same penalties apply.\r\n\r\n[/tab]\r\n\r\n[/tabs]',NULL),(8,577,0,NULL,0,2,'[minibox]\n[h2]Steamwheedle Cartel[/h2]\n[faction=21]\n[faction=577]\n[faction=369]\n[b]Everlook[/b]\n[/minibox]\n\n[b]Everlook[/b], the faction of the town Everlook, is a trading post is run by the goblins of the Steamwheedle Cartel. It lies at the crossroads of [zone=618]\'s main trade routes.\n\n[h3]General Information[/h3]\nThis town is the last point of civilization before reaching Hyjal Summit. It is run by goblins as a trading post and is officially neutral to all races and factions. Even so, pilgrims allowed to venture up to the World Tree stop here, but otherwise this is the highest that merchants and explorers may venture without the night elves’ permission. Everlook would offer a commanding view of Kalimdor, if it were not at such a high altitude that clouds constantly shroud the mountain’s lower flanks.\n\nEverlook is the only major goblin outpost in northern Kalimdor, and it serves several purposes. First, it serves as the base of operations for goblin thorium and arcanite miners since Winterspring has some of the few untapped veins of those materials on the continent. Second, it serves as a center of trade between the Alliance and the Horde. While Everlook is hardly as safe as Moonglade, generally the Alliance and the Horde treat each other fairly well there. Additionally, Everlook is a frequent stop-off and resupply point for the faithful who make the pilgrimage through Winterspring to Hyjal Summit.\n\n[h3]Reputation[/h3]\nReputation for Everlook and the Steamwheedle Cartel is mostly gained from quests in Winterspring. Having a friendly or higher reputation will make the guards help you in case of initiated violence against you.',NULL),(NULL,NULL,0,'talent-calculator',0,2,'[menu tab=2 path=2,13,4]Can\'t find the answer you were looking for? Just [url=/?aboutus#contact]contact us[/url], or post on our [url=/?forums&board=1]forums[/url]! \r\n\r\n[toc]\r\n\r\n[h2]General Usage[/h2]\r\n[ul]\r\n[li][screenshot url=STATIC_URL/images/help/talent-calculator/glyphs.jpg thumb=STATIC_URL/images/help/talent-calculator/glyphs2.jpg width=268 height=218 float=right][/screenshot][b]Selecting a class[/b] - Easily select a class\' talent tree by chosing from the class icon at the top, or from the dropdown menu. Clicking on a class\' name at the top left of the calculator will open that class\' page here on on this site, providing even more detailed information![/li] \r\n[li][b]Adding or removing talent points[/b] - To add points in a talent simply click the appropriate talent. To remove points, you can either right-click (or Shift+click) the talent.[/li]\r\n[li][b]Adding glyphs[/b] - Click on an empty glyph slot to open a picker window from which you can make your selection. To remove a glyph, simply right-click (or Shift+click) that glyph.[/li]\r\n[li][b]Linking to a build[/b] – Simply copy the auto-updating URL from your browser\'s address bar.[/li]\r\n[/ul]\r\n\r\n[h2]Tools + Options[/h2]\r\n[ul]\r\n[li][b]Reset all[/b] - Resets all talents across all trees.[/li]\r\n[li][img src=STATIC_URL/images/help/talent-calculator/options.jpg float=right][b]Reset tree[/b] - Clicking the red X at the top right corner of a talent tree will reset all talents in that particular tree. Other trees will not be reset.[/li]\r\n[li][b]Lock / Unlock[/b] - Locks or unlocks the talent build, preventing (or allowing) changes to be made. Linking to a build will automatically lock talents.[/li]\r\n[li][b]Import[/b] – Displays a pop-up text window where you can enter the URL of a talent build made with [url=http://www.wowarmory.com/talent-calc.xml]Blizzard\'s talent calculator[/url]. Be sure that you first select the \"Link to this build\" option in the Blizzard talent calculator so that the URL will be properly formatted for importing.[/li]\r\n[li][b]Print[/b] - Opens up a new, printer-friendly page with a textual representation of your chosen talents. Nice if you want to paste the talents you\'ve chosen somewhere, and would prefer it written out.[/li]\r\n[li][b]Link[/b] - Locks your chosen talents and creates a link to your build. Use this option to easily create a URL to share your build with others![/li]\r\n[/ul]\r\n\r\n[h2]Useful Tips[/h2]\r\n\r\n[ul]\r\n[li]When the calculator is locked, you can click talents and glyphs to view their corresponding spell or item page.[/li]\r\n[li]If you\'re building a third-party application, you can link to our talent calculator by using Blizzard-style URLs such as:\r\n[code]HOST_URL?talent#hunter-512002015051122431005311500053052002300100000000000000000000000000000000000000000[/code][/li]\r\n[/ul]',NULL),(NULL,NULL,0,'modelviewer',0,2,'[menu tab=2 path=2,13,1]\r\n\r\n[url=item=35350][img src=STATIC_URL/images/help/modelviewer/ss-viewin3d.gif float=right][/url]Aowow has a model viewer that will let you see the items and NPCs in the game in full 3D!\r\n\r\nYou can use the dropdown menus to select which character model you want to display armor pieces on, and the model viewer will remember your choice.\r\n\r\nThere are two different versions of the model viewer available, one written in Flash, and the other one written in Java. Aowow should remember which version you used last time, and will automatically open that model viewer the next time you click on the \"View in 3D\" button.\r\n\r\nIf you have any issues, please report them [url=/?forums&topic=202524]here[/url]!\r\n\r\n[i]Tip: You can close the box by clicking anywhere outside of the box.[/i]\r\n\r\n[h2]Modes[/h2]\r\n\r\n[tabs name=mode]\r\n\r\n[tab name=Flash]\r\n\r\n[url=item=34092][img src=STATIC_URL/images/help/modelviewer/ss-flash.png float=right][/url]The [b]Flash[/b] viewer is simple, quick to load, and should work on nearly all browsers. The Flash viewer is the default viewer, and all models will automatically load in the Flash Viewer unless you specify otherwise.\r\n\r\nIt requires the latest version of [url=http://www.adobe.com/go/BONRN]Flash[/url] to be installed on your computer.\r\n\r\n[h3]Controls[/h3]\r\n[ul]\r\n[li][b]Rotate[/b] – Click and drag / arrow keys[/li]\r\n[li][b]Zoom[/b] – Mousewheel / A & Z keys[/li]\r\n[/ul]\r\n\r\n[h3]Features[/h3]\r\n[ul]\r\n[li]Motion blur[/li]\r\n[li]Full screen mode[/li]\r\n[/ul]\r\n\r\n[/tab]\r\n\r\n[tab name=Java]\r\n\r\n[url=/?item=35350][img src=STATIC_URL/images/help/modelviewer/ss-java.png float=right][/url]The Java viewer is slower to initialize than the Flash Viewer, but once it\'s initialized it renders in [b]much greater[/b] detail. Most browsers will only need to initialize it once, and subsequent loads will be much faster. Some browsers may ask you to accept a security certificate when you initialize the viewer.\r\n\r\nIt requires the latest version of [url=http://jdl.sun.com/webapps/getjava/BrowserRedirect?locale=en&host=www.java.com]Java[/url] to be installed on your computer.\r\n\r\n[h3]Controls[/h3]\r\n[ul]\r\n[li][b]Rotate[/b] – Click and drag[/li]\r\n[li][b]Zoom[/b] – Mousewheel[/li]\r\n[li][b]Move[/b] – Right-click and drag[/li]\r\n[/ul]\r\n\r\n[h3]Features[/h3]\r\n[ul]\r\n[li]3D acceleration[/li]\r\n[li]Animations on NPCs, character models, small pets, and mounts[/li]\r\n[/ul]\r\n\r\n[/tab]\r\n\r\n[/tabs]\r\n',NULL),(NULL,NULL,0,'tooltips',0,2,'[menu tab=2 path=2,10]\r\n\r\n[div float=right align=right][url=http://wow.joystiq.com/2010/04/14/breakfast-topic-using-irl-irl/][img src=STATIC_URL/images/help/tooltips/ss-wowcom.png][/url]\r\n[small]Tooltips in action on [url=http://wow.joystiq.com/2010/04/14/breakfast-topic-using-irl-irl/]WoW Insider[/url][/small][/div]\r\n\r\nIt\'s never been easier to add tooltips to your site.\r\n\r\n[ol]\r\n[li]Add this piece of HTML code in the section of your page:\r\n[code][/code][/li]\r\n[li]You are done![/li]\r\n[/ol]\r\n\r\nLinks found on your site will now sport a [b]tooltip[/b] and an [b]icon[/b]. The following pages are supported: achievement, profile, item, npc, object, spell, quest. Icons show up by default, you can customize the colors of your links, and easily rename them!\r\n\r\nYou can check out this [url=STATIC_URL/widgets/power/demo.html]working demo[/url], and see how easy it is!\r\n\r\n[h2]Related[/h2]\r\n\r\n[tabs name=Related]\r\n\r\n[tab name=\"Advanced usage\"]\r\n\r\nOnce you have the [/code]\r\n[/tab]\r\n\r\n[tab name=\"XML feeds\"]\r\n\r\n[h3]Items[/h3]\r\nAlso available are our item XML feeds. Every item in the database has a corresponding XML feed. You can reach those feeds either by ID or by name. For example:\r\n\r\n[ul]\r\n[li]By ID: HOST_URL?item=52021&xml[/li]\r\n[li]By name: HOST_URL?item=iceblade%20arrow&xml[/li]\r\n[/ul]\r\n\r\n[/tab]\r\n\r\n[tab name=\"Other resources\"]\r\n\r\nInterested in using our script in your forum? Check out [url=http://wowhead.com/forums&topic=3464]this thread[/url] for information on implementing it on many popular forum systems (phpBB, vBulletin, etc.) or check out the handy guides written by Wowheads users:\r\n\r\n[ul]\r\n[li][url=http://wowhead.com/forums&topic=3464#p37094]vBulletin[/url][/li]\r\n[li]phpBB: [url=http://wowhead.com/forums&topic=3464#p37492]2.x.x[/url] - [url=http://wowhead.com/forums&topic=3464.6#p58403]2.x.x Mod Version[/url] | [url=http://wowhead.com/forums&topic=14347&p=126922]3.0[/url] [small]by craCkpot[/small] - [url=http://wowhead.com/forums&topic=3464#p37204]3.0[/url] [small]by marcimi[/small] - [url=http://wowhead.com/forums&topic=3464.3#p42858]3.0 Mod Version[/url][/li]\r\n[li][url=http://wowhead.com/forums&topic=3464#p37618]Simple Machines Forum (SMF)[/url][/li]\r\n[li][url=http://wowhead.com/forums&topic=3464.3&p=4080#p40631]Invision Power Board (IPB)[/url][/li]\r\n[li][url=http://wowhead.com/forums&topic=3464.3&p=42952#p42952]WordPress Blog[/url] ([url=http://wowhead.com/forums&topic=3464.4#p43652]Plugin Version[/url])[/li]\r\n[li][url=http://wowhead.com/forums&topic=3464.7&p=63338#p61443]PHP Nuke-Evolution[/url][/li]\r\n[li][url=http://wowhead.com/forums&topic=3464.3#p43232]MyBB[/url][/li]\r\n[li][url=http://wowhead.com/forums&topic=3464.6#p48648]TikiWiki[/url][/li]\r\n[li][url=http://wowhead.com/forums&topic=3464.6#p49640]YaBB[/url][/li]\r\n[li][url=http://wowhead.com/forums&topic=3464.5#p46801]Drupal[/url][/li]\r\n[li][url=http://wowhead.com/forums&topic=3464.3#p42456]PunBB[/url][/li]\r\n[li][url=http://wowhead.com/forums&topic=10938]Dojo[/url][/li]\r\n[/ul]\r\n\r\n[/tab]\r\n\r\n[/tabs]',NULL),(NULL,NULL,0,'searchbox',0,2,'[menu tab=2 path=2,16]\r\n\r\nThe code below will produce an iframe that contains the Aowow logo and a search box.\r\n\r\n[code]\r\n[/code]\r\n\r\n[h3]Parameters[/h3]\r\n\r\n[ul]\r\n[li][b]aowow_searchbox_format[/b] – String that specifies how big the iframe should be. The following values can be used:\r\n[pad]\r\n[table width=100%]\r\n[tr]\r\n[td width=20% align=center valign=top]\r\n\"160x200\"\r\n[img src=STATIC_URL/images/help/searchbox/preview-160x200.png]\r\n[/td]\r\n[td width=20% align=center valign=top]\r\n\"120x200\"\r\n[img src=STATIC_URL/images/help/searchbox/preview-120x200.png]\r\n[/td]\r\n[td width=20% align=center valign=top]\r\n\"160x120\"\r\n[img src=STATIC_URL/images/help/searchbox/preview-160x120.png]\r\n[/td]\r\n[td width=20% align=center valign=top]\r\n\"150x120\"\r\n[img src=STATIC_URL/images/help/searchbox/preview-150x120.png]\r\n[/td]\r\n[td width=20% align=center valign=top]\r\n\"120x120\"\r\n[img src=STATIC_URL/images/help/searchbox/preview-120x120.png]\r\n[/td]\r\n[/tr]\r\n[/table]\r\n[/li]\r\n[/ul]\r\n\r\n[h3]Tips[/h3]\r\n\r\n[ul]\r\n[li]You can style the iframe (e.g. adding a border) by using the following class name in your CSS code:\r\n[code].aowow-searchbox { ... }[/code][/li]\r\n[/ul]',NULL),(NULL,NULL,0,'searchplugins',0,2,'[menu tab=2 path=2,8]\r\n\r\n[div float=right align=right][img src=STATIC_URL/images/help/searchplugins/ss-searchsuggestions.png]\r\n[small]Also features search suggestions![/small]\r\n[/div]\r\n\r\nSearch plugins make it easy to search the database right from your browser!\r\n\r\n[toc h3=false]\r\n\r\n[h2][img src=STATIC_URL/images/help/searchplugins/firefox.gif border=0 margin=5 float=left][img src=STATIC_URL/images/help/searchplugins/ie.gif border=0 float=left]Firefox / Internet Explorer[/h2]\r\n\r\n[div clear=left][/div]Click on the button below to install the search plugin in your browser.\r\n\r\n[pad]\r\n\r\n[script]\r\nfunction addPlugin()\r\n{\r\n try {\r\n if(!$.browser.msie && !$.browser.mozilla) {\r\n throw(\'FAIL\');\r\n }\r\n\r\n window.external.AddSearchProvider(\'STATIC_URL/download/searchplugins/aowow.xml\');\r\n }\r\n catch(e)\r\n {\r\n alert(\'This feature is only for Firefox 2+ and Internet Explorer 7+.\');\r\n }\r\n}\r\n[/script]\r\n\r\n[html]Install pluginInstall plugin[/html]\r\n\r\n[div clear=left][/div][pad]\r\n\r\n[h2][img src=STATIC_URL/images/help/searchplugins/opera.gif border=0 float=left]Opera[/h2]\r\n\r\n[div clear=left][/div]\r\n\r\n[ul]\r\n[li]Right-click on the search box on the [url=/]homepage[/url].[/li]\r\n[li]Select \"Create Search\" in the menu.[/li]\r\n[li]Fill the form as follows:\r\n[pad]\r\n[img src=STATIC_URL/images/help/searchplugins/ss-opera.png border=0]\r\n[pad][/li]\r\n[li]Save your changes, and you\'ll be able to perform Aowow searches by typing \"wh\" followed by the search terms in the address bar (e.g. wh sword).[/li]\r\n[/ul]\r\n',NULL),(NULL,NULL,2,'page-not-found',0,2,'[tooltip name=AO815][b][color=q4]AO-815 Moteur Principal de Stabulation[/color][/b]\n[color=white]Lié lorsque utilisé\nUnique[/color]\n[color=q2]Utilise: Appelle le pouvoir de l\'Interwebs pour\ninvoquer l\'information demandé à Aowow.[/color]\n[color=q]\"En tout cas, c\'est ce que c\'est supposé faire...\"[/color][/tooltip]Quoi? Comment avez-vous... oubliez ça!\n\nIl semblerait que la page demandée n\'ait pas été trouvée. En tout cas, pas dans cette dimension.\n\nPeut-être que quelques réglages au [span class=tip tooltip=AO815][color=q4][u][AO-815 Moteur Principal de Stabulation][/u][/color][/span] pourraient résulter en l\'apparition soudaine de la page![pad][pad]\n\nOu vous pouvez essayer de [url=?aboutus#contact]nous contacter[/url] - la stabilité du AO-815 est discutable et vous ne voudriez pas un autre accident...\n\n[h2]Liens[/h2]\n[ul]\n[li]Retour à la [url=?]page d\'accueil[/url][/li]\n[li][url=?forums&board=1]Forum[/url] de feedback[/li]\n[/ul]',NULL),(NULL,NULL,0,'faq',0,2,'[small]no questions have been asked yet[/small]\r\n\r\nbesides .. yes, i\'m insane.',NULL),(NULL,NULL,0,'whats-new',0,2,'[small]this page for example[/small]',NULL),(NULL,NULL,0,'aboutus',0,2,'[h3]This is [s]Sparta![/s] [u]Aowow[/u][/h3]\r\n\r\nA project for private servers to sensibly display the vast amount of data a private server contains.\r\n\r\nBuilt with TrinityCore in my neck, but i\'m trying to get away from that .. some time.\r\nWith it\'s own data structure it shouldn\'t be too hard to write a converter for MaNGOS, Ascent or whatever software you prefere.\r\n\r\nThe expected version is 3.3.5 (12340), everything else will get messy.',NULL),(NULL,NULL,3,'page-not-found',0,2,'[tooltip name=AO815][b][color=q4]AO-815 Großkonfabulierungsmaschine[/color][/b]\n[color=white]Bei Benutzung gebunden\nEinzigartig[/color]\n[color=q2]Benutzen: Ersucht die Mächte der Internetze darum,\nAowow die benötigten Informationen zukommen zu lassen.[/color]\n[color=q]\"Das sollte es im Prinzip eigentlich tun...\"[/color][/tooltip]Was? Wie hast du... vergesst es!\n\nAnscheinend konnte die von Euch angeforderte Seite nicht gefunden werden. Wenigstens nicht in dieser Dimension.\n\nVielleicht lassen einige Justierungen an der [span class=tip tooltip=AO815][color=q4][u][AO-815 Großkonfabulierungsmaschine][/u][/color][/span] die Seite plötzlich wieder auftauchen![pad][pad]\n\nOder, Ihr könnt es auch [url=?aboutus#contact]uns melden[/url] - die Stabilität des AO-815 ist umstritten, und wir möchten gern noch so ein Problem vermeiden...\n\n[h2]Links[/h2]\n[ul]\n[li]Zur [url=?]Titelseite[/url] zurückkehren[/li]\n[li][url=?forums&board=1]Forum[/url] für Rückmeldungen[/li]\n[/ul]',NULL),(NULL,NULL,6,'page-not-found',0,2,'[tooltip name=AO815][b][color=q4]Dispositivo de confabulación suprema AO-815[/color][/b]\n[color=white]Se liga al usar\nÚnico[/color]\n[color=q2]Uso: Clama a los poderes de Internet para\ninvocar información requerida a Aowow.[/color]\n[color=q]\"Al menos, eso es lo que se supone que hace...\"[/color][/tooltip]¿Pero qué? ¿Cómo? .... ¡olvídalo!\n\nParece que la página que buscas no pudo ser encontrada. Al menos, no en esta dimensión.\n\n¡Quizá un par de ajustes al [span class=tip tooltip=AO815][color=q4][u][Dispositivo de confabulación suprema AO-815][/u][/color][/span] puede que hagan que la página aparezca de repente![pad][pad]\n\nO, puedes intentar [url=?aboutus#contact]contactar con nosotros[/url] - la estabilidad del AO-815 es debatible y no queremos otro accidente...\n\n[h2]Enlaces[/h2]\n[ul]\n[li]Volver a la [url=?]página principal[/url].[/li]\n[li]Foro del [url=?forums&board=1]feedback[/url].[/li]\n[/ul]',NULL),(NULL,NULL,0,'page-not-found',0,2,'[tooltip name=AO815][b][color=q4]AO-815 Major Confabulation Engine[/color][/b]\n[color=white]Binds when used\nUnique[/color]\n[color=q2]Use: Calls on the powers of the Interwebs to\nsummon requested information to Aowow.[/color]\n[color=q]\"At least, that\'s what it\'s supposed to do...\"[/color][/tooltip]What? How did you... nevermind that!\n\nIt appears that the page you have requested cannot be found. At least, not in this dimension.\n\nPerhaps a few tweaks to the [span class=tip tooltip=AO815][color=q4][u][AO-815 Major Confabulation Engine][/u][/color][/span] may result in the page suddenly making an appearance![pad][pad]\n\nOr, you can try [url=?aboutus#contact]contacting us[/url] - the stability of the AO-815 is debatable, and we wouldn\'t want another accident...\n\n[h2]Links[/h2]\n[ul]\n[li]Return to the [url=?]homepage[/url][/li]\n[li]Feedback [url=?forums&board=1]forum[/url][/li]\n[/ul]',NULL),(NULL,NULL,0,'markup-guide',0,2,'[menu tab=2 path=2,13,7]Here we have quite a few nifty markup tags that users can insert into their comments and forum posts to improve the style and easily link to database entries! Many of these tags can easily inserted using the corresponding icon or dropdown menu found above the text box. We\'ve put together this quick reference for all of these handy tags for you guys so you can get on your way to making high quality posts and comments!\n\n[h2]Formatting Tags[/h2]\n[h3]Bold[/h3]\n\\[b]text[/b]\n\n[h3]Line break[/h3]\n\\[br] -> inserts a line break.\n\n[h3]Code[/h3]\n\\[code]text[/code] -> creates a block of text that ignores markup and uses a monospace font.\n\n[h3]Horizontal Rule[/h3]\n\\[hr] -> creates a horizontal rule\n\n[h3]Italics[/h3]\n\\[i]text[/i] -> [i]text[/i]\n\n[h3]Preformatted text[/h3]\n\\[pre]text[/pre] -> shows text with all whitespace preserved in a monospace font, but allows markup\n\n[h3]Strikethrough[/h3]\n\\[s]text[/s] -> [s]text[/s]\n\n[h3]Small text[/h3]\n\\[small]text[/small] -> [small]text[/small]\n\n[h3]Subscript[/h3]\n\\[sub]text[/sub] -> [sub]text[/sub]\n\n[h3]Superscript[/h3]\n\\[sup]text[/sup] -> [sup]text[/sup]\n\n[h3]Underline[/h3]\n\\[u]text[/u] -> [u]text[/u]\n\n[h2]Database Tags[/h2]\n\n\n[b]For all database tags:[/b]\nOptional attributes: site/domain (both work identically, only use one)\nValid options are: www (default), en, de, es, fr, ru.\nThe purpose of these is to link to localized versions of items with the pretty db tags.\n[b]Example:[/b] \\[achievement=3579 domain=ru] -> [achievement=3579 domain=ru] \n\n[h3]Achievements[/h3]\n\\[achievement=3579] -> [achievement=3579]\n\n[h3]Classes[/h3]\n\\[class=11] -> [class=11]\n\n[h3]Events[/h3]\n\\[event=1] -> [event=1]\n\n[h3]Factions[/h3]\n\\[faction=749] -> [faction=749]\n\n[h3]Items[/h3]\n\\[item=12345] -> [item=12345]\n\nTo hide the icon: \\[item=12345 icon=false] -> [item=12345 icon=false]\n\n[h3]Itemsets[/h3]\n\\[itemset=699] -> [itemset=699]\n\n[h3]NPCs[/h3]\n\\[npc=32906] -> [npc=32906]\n\n[h3]Objects[/h3]\n\\[object=1733] -> [object=1733]\n\n[h3]Pets[/h3]\n\\[pet=45] -> [pet=45]\n\n[h3]Quests[/h3]\n\\[quest=7981] -> [quest=7981]\n\n[h3]Races[/h3]\n\\[race=11] -> [race=11]\n\n[b]To specify the gender of the icon:[/b] \\[race=11 gender=1] -> [race=11 gender=1] - 0 is male, 1 is female\n\n[h3]Skills[/h3]\n\\[skill=171] -> [skill=171]\n\n[h3]Spells[/h3]\n\\[spell=52398] -> [spell=52398]\n\\[spell=31565 buff=true] -> [spell=31565 buff=true]\n\n[h3]Statistics[/h3]\n\\[statistic=1076] -> [statistic=1076]\n\n[h3]Zones[/h3]\n\\[zone=3959] -> [zone=3959]\n\n[h2]HTML Tags[/h2]\n\n[h3]Anchor[/h3]\n\\[anchor=text] -> creates an anchor with the name \\\"text\\\" at this point.\n\n[h3]Ordered List[/h3]\n\\[ol]\\[li]list item[/li][/ol] -> [ol][li]list item[/li][/ol]\n\n[h3]Tables[/h3]\n[b]\\[table][/b]\nBorder: \\[table border=2]\nSpacing: \\[table cellspacing=2]\nPadding: \\[table cellpadding=2]\nWidth: \\[table width=500px] - Valid units are px, em, %\n\n[b]\\[tr][/b] - No attributes\n\n[b]\\[td][/b]\nAlign: \\[td align=right] - Valid options are left, right, center, justify\nVertical align: \\[td valign=baseline] - Valid options are top, middle, bottom, baseline\nColumn span: \\[td colspan=2]\nRow span: \\[td rowspan=2]\nWidth: \\[td width=500px] - Valid units are px, em, %\n\n[h3]Unordered List[/h3]\n\\[ul]\\[li]list item[/li][/ul] -> [ul][li]list item[/li][/ul]\n\n[h3]URLs[/h3]\n\\[url=http://www.wowhead.com]Wowhead[/url] -> [url=http://www.wowhead.com]Wowhead[/url]\n\\[url]http://www.wowhead.com[/url] -> [url]http://www.wowhead.com[/url]\n\\[url=http://www.google.com rel=item=12345]Rel link[/url] -> [url=http://www.google.com rel=item=12345]Rel link[/url]',NULL),(8,589,0,NULL,0,2,'The [b]Wintersaber Trainers[/b] is an Alliance-only faction consisting of only two night elven NPCs that can both be found in [zone=618]. Currently, the only questgiver is [npc=10618], who is located at the top of Frostsaber Rock in Winterspring. Upon reaching exalted with this faction, Rivern will sell a special mount, the [item=13086].\n\nThis faction\'s mount is the only epic mount (100% riding speed) attainable in the game which only requires 75 riding skill (and thus only costs 90 Gold). The faction is noted for having no Horde counterpart and having the longest and most repetitive reputation grind of the entire game. The first quest can be attained at level 58, while the other two are attainable at level 60.\n\n[h3]Reputation[/h3]\nReputation with the Wintersaber Trainers can only be obtained through three repeatable quests. There are no faction items or mobs that reward repuation directly.\n\n[b]Neutral 0 to 1500[/b]\nOnly one repeatable quest will available at first, so until neutral 1500/3000 is reached the [quest=4970] quest should be repeated. Any Shardtooth and Chillvind mob in Winterspring will drop these. This quest should be done solo as the drop rates are low and not shared if others have the quest.\n\n[b]Neutral 1500 to Exalted[/b]\nHalfway through neutral the [quest=5201] quest will be available. This quest requires to kill 10 Winterfall mobs in the Winterfall Village, just east of Everlook. If the quest [quest=8464] has been done with the [faction=576], [item=21383] can drop from the Winterfall mobs. If a player wants both reputations, saving these until revered with Timbermaw Hold will result in a lot of \"free\" reputation.\n\nThis quest can be done in groups for increased speed. Players grinding either Wintersaber Trainers or Timbermaw Hold reputation can often be found in the Winterfall Village. Even with an epic mount, the travel to and from Winterfall Village takes up much time. There are tigers among the route who will daze you, which will result in a demount, this should be avoided (but can be hard as they\'ll catch up with you on a 60% mount). Usually this quest is repeated all the way to exalted, ignoring the third quest. \n\n[b]Honored to Exalted[/b]\nAt honored the third quest [quest=5981] is available. The quest requires the player to kill 8 Frostmaul giants. They are a lot harder than the Winterfall mobs and the travel lengths are quite longer. This quest is usually skipped, and instead Winterfall Intrusion is repeated.\n\nDue to some players grinding Timbermaw Hold reputation, in Winterfall Village among other places, this quest can indeed turn out to be a faster reputation reward than the Winterfall Intrusion one.',NULL),(8,609,0,NULL,0,2,'The [b]Cenarion Circle[/b] is an organization of druids, both tauren and night elf, named after Cenarius. Its members are dedicated to protecting nature and restoring the damage done to it by malevolent forces.\n\nThe Circle has many posts, but their main home is the town of Nighthaven in the [zone=493]. Druids learn the spell [spell=18960] at level 10, but anyone else will have to make it to [zone=361] and find a way through the Timbermaw Furbolg tunnels.\n\nThe Circle\'s other major presence is in [zone=1377], where they combat the Silithid, the Qiraji, and Twilight\'s Hammer. Valor\'s Rest and Cenarion Hold serve as their bases in the hostile land, and offer many opportunities to adventurers seeking to aid the druids.\n\n[h3]Notable Members[/h3]\n[ul][li][npc=11832], son of Cenarius[/li][li][npc=3516], leader of the night elven druids[/li][li][npc=5769], leader of the tauren druids[/li][/ul]\n\n[h3]Reputation[/h3]\nThere are several ways to gain reputation with the Cenarion Circle. Aside from the available [url=?quests&filter=cr=1;crs=609;crv=0]quests[/url], you may do the following to gain reputation:[ul][li]Raid the [zone=3429]. This is by far the fastest way to gain reputation, as a full clear can net over 2000 reputation.[/li][li]Kill twilight cultists. These stop yielding reputation when you reach the end of friendly for [npc=11880] and [npc=11881], and at the end of honored for [npc=15201].[/li][li]Turn in [item=20404]. These drop off the cultists, and yield 250 reputation for 10 texts.[/li][li]Turn in [item=20513], [item=20514], and [item=20515]. These drop off the minibosses that are summoned at the windstones using the [itemset=492].[/li][li]Perform the [quest=8507]. These are either [url=?search=logistics+task+briefing]Logistics quests[/url], [url=?search=combat+task+briefing]Combat quests[/url], or [url=?search=tactical+task+briefing]Tactical quests[/url]. The badges you earn from these quests may then be turned in for additional reputation, if you chose to forsake the rewards.[/li][li]Collect [object=181598] from the zone and turn it in to your faction NPC.[/li][/ul]',NULL),(8,729,0,NULL,0,2,'[b]Frostwolf Clan[/b], along with [npc=11946], lived along the [zone=36] practicing shamanism, and having Frost Wolves as their companions. The dwarven expedition known as the [faction=730] have started an expedition in the Frostwolf territory to excavate the valley and mine its veins, a transgression to the orcs who inhabited Alterac. This provoked a slaughter of the first expedition, and started the battle for [zone=2597].\n\n[h3]Reputation[/h3]\nPlayers can earn reputation in this faction by participating in the Alterac Valley battleground by doing various tasks as well as killing members of the opposite faction, the Stormpike Guard.\n\nYou are granted the player title [title=47] once exalted with the Frostwolf Clan and the other two battleground factions, [faction=889] and [faction=510].',NULL),(8,730,0,NULL,0,2,'[b]Stormpike Guard[/b] is the Alliance faction in the [zone=2597] battleground. They are an expedition of dwarves of the Stormpike Clan, native to the \"valleys of Alterac\" in [zone=36]. The Stormpikes\' search for relics of their past and harvesting of resources in Alterac Valley have led to open war with the the orcs of the [faction=729] dwelling in the southern part of the valley. They were also issued with a \"sovereign imperialistic imperative\" by [npc=2784] to take the valleys of Alterac for [zone=1537]. \n\nThe main Stormpike base is Dun Baldar, where their leader, [npc=11948], resides with his marshals. His second in command, [npc=11949], is found south of Dun Baldar, at Stonehearth Outpost.\n\n[h3]Reputation[/h3]\nPlayers can earn reputation in this faction by participating in the Alterac Valley battleground by doing various tasks as well as killing members of the opposite faction, the Frostwolf Clan.\n\nYou are granted the player title [title=48] once exalted with Stormpike Guard and the other two battleground factions, [faction=890] and [faction=509].',NULL),(8,749,0,NULL,0,2,'The [b]Hydraxian Waterlords[/b] are elementals that have made their home on the islands east of [zone=16]. Sworn enemies of the armies of [npc=11502]. Historically servants of the Old Gods, the four Elemental Lords served the gods with undying loyalty. The minions of Neptulon the Tidehunter were numerous and mindless. It is not yet known how [npc=13278] broke free of his lord\'s control (if indeed he has), or what is his ultimate goals are, but the Water elementals are the only elementals that do not attack the mortal races with abandonment.\n\nLocated on a remote island in the far east of Azshara, Duke Hydraxis offers some quests. The first two require killing various elementals in [zone=139] and [zone=1377]. Increased faction with the Waterlords opens up additional quests leading into the [zone=2717]. Any items obtained from the Hydraxian Waterlords, are obtained from its various quests.\n\nCompleting the questline allows players to obtain [item=17333] used to douse the runes found near most bosses in Molten Core. This is required to summon [npc=12018], the penultimate boss, and, after his defeat, to summon Ragnaros himself. Since there are seven runes, any raid needs at least seven players that bring a quintessence if they wish to finish the instance. Since most of the questline takes place within Molten Core, any raider can complete this task with little more than some traveling and an [zone=1583] run.\n\n[h3]Reputation[/h3]\nRepuation is gained through slaying the following elemental enemies of the waterlords.[ul][li][npc=11746] - 5 reputation, lasts until honored.[/li][li][npc=11744] - 5 reputation, lasts until honored.[/li][li][npc=7032] - 5 reputation, lasts until honored.[/li][li][npc=9017] - 15 reputation, lasts until revered.[/li][li][npc=14478] - 25 reputation, lasts until revered.[/li][li][npc=9816] - 50 reputation, lasts until revered.[/li][li][npc=11658], [npc=11673], [npc=12101] and [npc=11668] - 20 reputation, lasts until revered.[/li][li][npc=11659] and Lava Pack ([npc=12100], [npc=12076], [npc=11667], [npc=11666]) - 40 reputation, lasts until revered.[/li][li][npc=12118], [npc=11982], [npc=12259], [npc=12057], [npc=12056], [npc=12264], [npc=12098] - 100 reputation, lasts until exalted.[/li][li][npc=11988] - 150 reputation, lasts until the end of exalted.[/li][li][npc=11502] - 200 reputation, lasts until the end of exalted.[/li][/ul]Reaching revered status with the Hydraxian Waterlords allows players to obtain the [item=22754], which replenishes itself and thus eliminates the need to return to Hydraxis to obtain a new quintessence every week.',NULL),(8,809,0,NULL,0,2,'The [b]Shen\'dralar[/b] are the faction of the Night Elves remaining in [zone=2557]. They are a group of high practitioners of arcane magic in order of their former Queen Azshara, and her followers, the Highborne. They have been living in Eldre\'Thalas (previous name of Dire Maul) since the Great Sundering. They are few, but their knowledge and mystic power are great, referring to things players think are powerful such as [b]Arcanums[/b] and [b]Librams[/b] as mere cantrips.\n\nTheir leader, [npc=11486], was in charge and oversaw the construction of the pylons to contain the great demon [npc=11496] and syphon his demonic power. After many long years though, it began to dwindle so he started killing the remaining night elves to maintain energy. So their spirits come to adventurers and ask them to kill him. There are very few of the original inhabitants left alive.\n\n[h3]Reputation[/h3]\nReputation can be gained by turning repeatedly in the three Librams of Dire Maul ([item=18333], [item=18334], [item=18332]). Turning in the following class books also gives some reputation:[ul][li][item=18357] - Warrior[/li][li][item=18363] - Shaman[/li][li][item=18356] - Rogue[/li][li][item=18360] - Warlock[/li][li][item=18362] - Priest[/li][li][item=18358] - Mage[/li][li][item=18364] - Druid[/li][li][item=18361] - Hunter[/li][li][item=18359] - Paladin[/li][li][item=18401] - Warrior & Paladin[/li][/ul]Both class books and librams give 500 Reputation points each.',NULL),(8,889,0,NULL,0,2,'[b]Warsong Outriders[/b] is an orcish clan formerly led by [npc=18076], in which the clan was named after. The clan\'s Warsong Outriders form the Horde faction in the [zone=3277] battleground, where they are attempting to defend their logging operations in [zone=331] from the [faction=890].\n\nOne of the strongest and most violent clans, the Warsong Clan was also one of the most distinguished clans on Draenor and was able to evade Alliance expedition forces at every turn. Depicted as Grunts, they have mastered the use of swords and blades and a few of them have even attained the rank of a Blademaster.\n\n[h3]Reputation[/h3]\nReputation is gained through participation in the Warsong Gulch battleground. You gain 35 reputation each time your side captures a flag. This reputation gain is increased to 45 on holiday weekends.\n\nYou are granted the player title Conqueror once exalted with Warsong Outriders and the other two battleground factions, [faction=510] and [faction=729].',NULL),(8,890,0,NULL,0,2,'[b]Silverwing Sentinels[/b] are the Alliance faction for the [zone=3277] battleground. The night elves, who have begun a massive push to retake the forests of [zone=331] are now focusing their attention on ridding their land of the [faction=889] once and for all. And so, the Silverwing Sentinels have answered the call and sworn that they will not rest until every last orc is defeated and cast out of Warsong Gulch.\n\n[h3]Reputation[/h3]\nReputation is gained through participation in the Warsong Gulch battleground. You gain 35 reputation each time your side captures a flag. This reputation gain is increased to 45 on holiday weekends.\n\nYou are granted the player title [title=48] once exalted with Silverwing Sentinels and the other two battleground factions, [faction=730] and [faction=509].',NULL),(8,909,0,NULL,0,2,'The [b]Darkmoon Faire[/b] is a mysterious traveling carnival, which roams not only Azeroth but Outland as well. Led by the inimitable [npc=14823], a gnome of dubious heritage and unknown providence, the Faire brings fun, games, prizes, and exotic trinkets of unexpected power to [zone=215], [zone=12], or [zone=3519] each month.\n\nA variety of amusements can be had by the discerning fairegoer, but the most common attraction is the ticket redemption. A variety of merchants at the Faire collect items from around the worlds in exchange for [item=19182]. The tickets can, in turn, be saved up and turned in for prizes of varying worth and power. Several different ticket distributors are posted around the Faire, offering tickets for crafted items made by Leatherworkers, Blacksmiths, or Engineers as well as items gathered in the wild such as [item=11404] and [item=19933]. Tickets can be redeemed for many things, from flowers to hold in the off-hand to necklaces of great power.\n\nMany adventurers seek out the Darkmoon Faire to turn in the mystical [url=?items=15.0&filter=minle=1;cr=107;crs=0;crv=Combine+the+Ace]Darkmoon Cards[/url]. Darkmoon Cards come in eight suits, each of which has cards from Ace to Eight. Combining all cards in a suit produces a deck, which will start a quest to return that deck to the Darkmoon Faire. Each of the eight decks produces a different [url=?items=4.-4&filter=na=Darkmoon+Card]trinket[/url] with a different effect, some of which are quite powerful.\n\nThe Darkmoon Faire\'s usual schedule has it arriving on site on the first Friday of the month. For the weekend, the carnies will be seen setting up the midway, and the Faire will actually start early on the following Monday.',NULL),(8,910,0,NULL,0,2,'The [b]Brood of Nozdormu[/b] is a faction consisting of the Bronze Dragonflight. Their leader [npc=15192] can be found outside the [b]Caverns of Time[/b], with many of its agents flying in the sky of [zone=1377].\n\nIn order to open the gates of [b]Ahn\'Qiraj[/b], one champion must complete a long quest line for the bronze dragon Anachronos. This reputation is also relevant in the [zone=3428]; to obtain epic quest gear and rings.\n\n[h3]Reputation[/h3]\nPlayers begin at 0/36000 hated, the lowest level of reputation possible.\n\nBrood of Nozdormu reputation can be earned through killing bosses in both Ahn\'Qiraj instances, killing monsters inside the Temple of Ahn\'Qiraj, and doing quests related to the dungeons. You can also farm [item=20384], though this will take a lot longer, and requires one to have obtained the [item=20383] in [zone=2677] for the [item=21175] quest chain.\n\nKilling trash in the Temple of Ahn\'Qiraj can only get you to 2999 / 3000 Neutral, at which point reputation can only be further advanced through quests and handing in [item=21229] and [item=21230]. You may want to save all the insignias until after you are Neutral, since at that point gaining reputation becomes much more difficult.',NULL),(8,911,0,NULL,0,2,'[b]Silvermoon City[/b] is the capital of the blood elves, located in the northeastern part of the [zone=3430] within the kingdom of Quel\'Thalas. The breathtaking capital city of the blood elves may rival the dwarven capital of [zone=1537] as the world\'s oldest, still standing, capital. Recently rebuilt from the devastating blow dealt by the evil Prince Arthas, the city houses the largest population of blood elves left on Azeroth.[pad]Silvermoon today is only the eastern half of the original city; the western half was almost completely destroyed by the Scourge during the Third War. Falconwing Square, the second blood elf town, is the only part of western Silvermoon remaining in blood elf control. The Dead Scar (the path taken by Arthas Menethil and his undead army on the quest to resurrect Kel\'Thuzad, which carves through all of Eversong Woods) separates the rebuilt Silvermoon from the ruins of the western half. Interestingly, the Ruins of Silvermoon house no undead, instead they contain [url=?npcs&filter=na=wretched;maxle=8]Wretched[/url] and malfunctioning [npc=15638]. As it stands, what remains of Silvermoon City is still bigger than current Horde cities.\n\n[h3]History[/h3]\nThe city of Silvermoon was founded by the high elves after their arrival in Lordaeron thousands of years ago. The city was constructed out of white stone and living plants in the style of the ancient Kaldorei Empire. The city contained the famous Academies of Silvermoon as a center for the learning of Arcane Magic and Sunstrider Spire, a majestic palace home to the Royal family of the high elves. The Convocation of Silvermoon (also known as \"The Silvermoon Council\"), the ruling body of the high elves was also based here. Across a stretch of ocean to the north is the island that contains the Sunwell.[pad]Although Silvermoon itself was left relatively unscathed from the second war, in the third war the Death Knight Arthas led the Scourge into the city, attacking it on his quest to reach the Sunwell. The High Elven King was slain and the majority of the population killed. Scourge forces held the city for a time but abandoned it after the depleting of its resources.[pad]Though the city was attacked by the Scourge, it is not as destroyed as one might think. Though many of its plants are dead, and the occasional dead body is sprawled across the cobblestone, the city was immune to the fire and destruction. Silvermoon now resembles a ghost town, intact, but eerily abandoned. Nevertheless, treasure hunters often frequent Silvermoon to try and find some of the valuable artifacts that the elves left behind before they deserted the city, but the ghosts of Silvermoon\'s past inhabitants prevents anyone from taking anything.\n\n[h3]Reputation[/h3]\nA comprehensive list of quests that grant Silvermoon reputation can be found [url=?quests&filter=maxle=69;cr=1;crs=911;crv=0#00Mz]here[/url].[pad][npc=20612] is the quest giver for the repeatable [item=14047] quest that must be completed by non-blood elf Horde players in order to reach exalted and gain the ability to ride [url=?items=15.5&filter=na=hawkstrider]hawkstriders[/url], the mount of the blood elf race.',NULL),(8,922,0,NULL,0,2,'[b]Tranquillien[/b] is a joint blood elf and Forsaken town and separate faction in the [zone=3433].\n\n[h3]History[/h3]\nAs the Scourge made their way to the Sunwell, the elves had no choice but to retreat. The town of Tranquillien was abandoned by the retreating elves. The town is now used by the blood elves and the Forsaken as their base of operation to launch attacks aiming to take back the Ghostlands from the Scourge. However, the city is surrounded by the Scourge and even couriers have trouble getting past the enemy to reach the town. The undead forces of Deatholme are the most dangerous threat to the town.\n\n[h3]Reputation[/h3]\nUnlike most starting areas, the town of Tranquillien is its own faction. All quests you do for them will garner at least 1000 reputation apiece. [npc=16528] acts as the Tranquillien quartermaster. Vredigar can be found near the inn and will sell various [span class=q2]uncommon[/span] items, and even a [span class=q3]rare[/span] cloak when you reach exalted! If you complete all of the Tranquillien quests, you should be exalted by approximately level 20.[pad]There are a variety of quests mostly concerning reclaiming overrun villages, investigating undead and helping around. The \"end\" of the quest-revealed lore surrounding Tranquillien culminates with the quest to kill [npc=16329].',NULL),(8,930,0,NULL,0,2,'[b]Exodar[/b] is the faction associated with [zone=3557], the enchanted capital city of the draenei, built out of the largest husk of their crashed dimensional ship of the same name. It is located in the westernmost part of [zone=3524]. The Exodar faction leader is [npc=17468], who is located near the battlemasters in the Vault of Lights.\n\nThe history of the Exodar is a short one, as the draenei only recently raised it around the husk of their crashed ship, which is still smoking from the impact. The Exodar was once a naaru satellite structure around the dimensional fortress [url=?search=tempest+keep#z0z]Tempest Keep[/url]. The Exodar contains a large amount of technological wonders (due to its origins lying with the Tempest Keep) such as magically enchanted \"wires\" which transport holy energy throughout the ship to power the heating and lighting, as well as augmenting the draeneis\' already considerable powers.\n\n[h3]Reputation[/h3]\nAs with other major factions associated with the main races, Exodar reputation may be gained by doing repeatable cloth turn-in quests, killing the opposing faction in [zone=2597] (the blood elves), and doing the appropriately related quests. At honored, the player can purchase items from Exodar related vendors for 10% less, and at exalted, the player, if not a draenei, can purchase the [url=?items=15.5&filter=na=elekk;cr=93:92;crs=2:1;crv=0:0]various mounts[/url] sold by the Exodar. The cloth turn-in quests are available from [npc=20604] [small][/small].',NULL),(8,932,0,NULL,0,2,'[b]The Aldor[/b] are an ancient order of draenei priests who revere the naaru, and to this day they assist the naaru known as [faction=935] in their battle against [npc=22917] and the Burning Legion. They are found primarily in [zone=3703] and [zone=3520]. Though they have suffered much at the hands of the blood elves who later became [faction=934], they have put aside open warfare for the sake of the Sha\'tar. The Aldor\'s most holy temple lies on the Aldor Rise, overlooking the city from the west.\n\nMost players will start at neutral with the Aldor. [npc=18166] in Shattrath City will give players an initial quest to become friendly with the Aldor or the Scryers. This choice is reversible if players feel the need. Draenei players will be friendly with the Aldor and hostile with the Scryers, whereas blood elf players will be hostile to the Aldor and friendly to the Scryers.\n\n[npc=19321] and [npc=20807] are located in the Aldor bank on the northern edge of the Terrace of Light. The Shrine of Unending Light on Aldor Rise is home to [npc=20616]Asuur [small][/small] and [npc=21906] [small][/small], who exchange epic armor tokens for [url=?itemsets&filter=ta=12]Tier 4[/url] and [url=?itemsets&filter=ta=13]Tier 5[/url] gear, respectively.\n\n[i]Note: Reputation gains with Aldor correspond with a 10% greater loss of reputation with the Scryers. Most reputation gains with the Aldor will also grant 50% of the reputation gained toward your standing with the Sha\'tar.[/i]\n\n[h3]Reputation[/h3]\n[b]Until Honored[/b]\nPlayers looking to gain the higher reputation ranks (revered, exalted) may wish to save non-repeatable quests until after reaching honored.\n\nTurning in 10 [span class=q1][item=29425][/span] to [npc=18537] in Aldor Rise will grant 250 reputation with Aldor. There is also a repeatable quest for single mark turn-ins which yields 25 rep. These marks drop from low ranking Burning Legion members found in most zones in Outland, including the two camps north of Auchindoun in the Bone Wastes of [zone=3519]. Approximately 240 marks are required to go from friendly to honored. In addition these quests provide Sha\'tar reputation; 125 reputation per 10 or 12.5 reputation per single turn in.\n\nPlayers who also desire [faction=978] or [faction=941] reputation may prefer killing orcs at Kil\'Sorrow Fortress in southeastern [zone=3518], as they yield marks as well as 10 Kurenai or Mag\'har reputation per kill.[pad][b]Until Exalted[/b]\nOnce you reach level 68 you may also turn in [span class=q1][item=30809][/span] at the same rates as Marks of Kil\'jaeden. These drop from high-ranking followers of the Burning Legion. If you wish, you may turn in the higher level marks before honored reputation. In [zone=3522], grinding in Death\'s Door is the most compact group of mobs that drop marks.[pad][b]Fel Armaments[/b]\n[span class=q2][item=29740][/span] may be turned in at any time to [npc=18538]Ishanah [small][/small] inside the Shrine of Unending Light on the Aldor Rise. This will increase your reputation with Aldor by 350 per hand-in. In addition to reputation gains, you will receive [span class=q1][item=29735][/span], which is currency for the purchase of shoulder enchants from Inscriber Saalyn in the Aldor bank.\n\n[h3]Switching to Aldor[/h3]\nTo change your faction from the Scryers to the Aldor to access their crafting recipes (and undo all reputation progress you have made), find [npc=18597], an Aldor in Lower City. She offers a repeatable quest for 8x [span class=q1][item=25802][/span]. Once you are neutral with the Aldor, you may no longer receive this quest.',NULL),(8,933,0,NULL,0,2,'Led by [npc=19674], [b]The Consortium[/b] are ethereal smugglers, traders and thieves that have come to Outland. Their main base of operations and biggest settlement is the Stormspire, but they can be found at Midrealm Post, the Aeris Landing, within the [zone=3792] of Auchindoun and various other places.\n\nUpon reaching Friendly status, players are officially considered members of the Consortium and given a salary. The salary is a bag of gems at the beginning of every month, given by [npc=18265] at Aeris Landing. Higher reputation with the Consortium yields higher qualities and quantities of jewels each month.\n\n[h3]Reputation[/h3]\n[b]Until Friendly[/b][ul][li]Run Mana-Tombs in [i]normal[/i] mode, ~1200 reputation per run.[/li][li]Turn in [item=25416] at [npc=18265].[/li][li]Turn in [item=25463] at [npc=18333].[/li][/ul][b]Friendly to Honored[/b][ul][li]Run Mana-Tombs in [i]normal[/i] mode, ~1200 reputation per run.[/li][li]Turn in [item=25433] at [npc=18265].[/li][li]Turn in [item=29209] at [npc=19880].[/li][/ul][b]Honored to Exalted[/b][ul][li]Run Mana-Tombs in [i]heroic[/i] mode, ~2400 reputation per run.[/li][li]Complete all available [url=?quests&filter=cr=1;crs=933;crv=0]quests[/url].[/li][li]Turn in [item=25433] at [npc=18265].[/li][li]Turn in [item=29209] at [npc=19880].[/li][/ul]Characters trying to simultaneously earn reputation with the [faction=941] or [faction=978] and the Consortium may want to focus on killing ogres ([url=?npcs&filter=na=boulderfist;cr=6;crs=3518;crv=0]Boulderfist[/url], [url=?npcs&filter=na=Warmaul;cr=6;crs=3518;crv=0]Warmaul[/url]) in Nagrand and saving the Obsidian Warbeads for Consortium turn-ins. The only caveat is the drop rate, which is roughly 33% for the warbeads, while it is 50% on the insignias. If you are level 70 and want a faster grind without concern for Mag\'har/Kurenai reputation, then you may want to grind insignias instead. Then again, the ogres are generally easier to grind, ranging from level 65 to 67. The choice is ultimately up to the player.',NULL),(8,934,0,NULL,0,2,'[b]The Scryers[/b] are blood elves who reside in [zone=3703] led by [npc=18530]. The group broke away from [npc=19622] and offered to assist the Naaru at Shattrath City. They are at odds with the [faction=932], and compete with them for power within Shattrath and the Naaru\'s favor.[pad]Most players will start at neutral with the Aldor. [npc=18166] in Shattrath City will give players the choice of aligning themselves with the Scryers or Aldor after completing [quest=10211]. This choice is reversible if players feel the need. Blood elf players will be friendly with the Scryers and hostile with the Aldor, whereas draenei players will be hostile to the Scryers and friendly to the Aldor.[pad]The Scryers have both a [npc=19251] trainer and a [npc=19252] trainer. Due to this, the enchanter nestled deep within [zone=1337] is rendered obsolete.[pad][npc=19331] and [npc=20808] are located in the Scryers bank on the southern edge of the Terrace of Light. The Seer\'s Library in the Scryer\'s Tier is home to [npc=20613] [small][/small] and [npc=21905] [small][/small], who exchange epic armor tokens for [url=?itemsets&filter=ta=12]Tier 4[/url] and [url=?itemsets&filter=ta=13]Tier 5[/url] gear, respectively.[pad][i]Note: Reputation gains with Scryers correspond with a 10% greater loss of reputation with the Aldor. Most reputation gains with the Scryers will also grant 50% of the reputation gained toward your standing with the [faction=935].[/i]\n\n[h3]Lore[/h3]\nAfter enduring relentless assaults, the harried Sha\'tar and Aldor guards braced for the next wave as it marched over the horizon. This time, the attack came from the armies of [npc=22917]. A large regiment of blood elves had been sent by Illidan’s ally, Prince Kael\'thas Sunstrider, to lay waste to the city. As the regiment of blood elves crossed the bridge, the Aldor’s exarches and vindicators lined up to defend the Terrace of Light. Then the unexpected happened, the blood elves laid down their weapons in front of the city\'s defenders. Their leader, a blood elf elder known as Voren’thal, stormed into the Terrace of Light and demanded to speak to the naaru [npc=18481]. As the naaru approached him, Voren’thal knelt and uttered the following words: \"I’ve seen you in a vision, naaru. My race’s only hope for survival lies with you. My followers and I are here to serve you.\"[pad]The defection of Voren’thal and his followers was the largest loss ever incurred by Kael’thas’ forces. Many of the strongest and brightest amongst Kael’thas’ scholars and magisters had been swayed by Voren’thal\'s influence. The naaru accepted the defectors who became known as the Scryers.\n\n[h3]Reputation[/h3]\n[b]Until Honored[/b]\nPlayers looking to gain the higher reputation ranks (revered, exalted) may wish to save non-repeatable quests until after reaching honored.[pad]Turning in 10 [span class=q1][item=29426][/span] to [npc=18531] in Scryer\'s Tier will grant 250 reputation with the Scryers. These signets can also be turned in one at a time at the same exchange rate, 25 reputation per signet. These signets drop from low ranking Firewing members found in the northeast section of Terrokar Forest. This repeatable quest becomes unavailable at honored. If no other reputation quests are done, 240 signets are required to go from friendly to honored.[pad][b]Until Exalted[/b]\nOnce you reach level 68, you may also turn in [span class=q1][item=30810][/span]. These drop from high-ranking Sunfury blood elves (found in [zone=3523], [zone=3520], and the [url=?search=tempest+keep+-eye+-kael]Tempest Keep[/url] instances). If you wish, you may turn in the higher level signets before honored reputation, however it is recommended that you save them for after you hit honored. For every 10 signets, you will gain 250 reputation. Once you hit honored it will take approximately 1,320 Sunfury signets to go from honored to exalted if no other reputation is earned.[pad][b]Arcane Tomes[/b]\n[span class=q2][item=29739][/span] may be turned in at any time to Voren\'thal the Seer inside the The Seer\'s Library on the Scryer\'s Tier. This will increase your reputation with the Scryers by 350 per hand-in. If you wish, you may turn in the Arcane Tomes before honored reputation, however it is recommended that you save them for after you hit honored. Once you hit honored it will take approximately 94 Arcane Tomes to go from honored to exalted if no other reputation is earned. In addition to reputation gains, you will receive an [span class=q1][item=29736][/span], which is currency for the purchase of shoulder enchants from Inscriber Veredis, who resides in the Scryers bank.\n\n[h3]Switching to Scryers[/h3]\nTo change your faction from Aldor to Scryers to access their crafting recipes (and undo all reputation progress you have made), find [npc=18596], a Scryers in the Lower City. She offers you a repeatable quest, [quest=10024], that requires you to find eight [span class=q1][item=25744][/span]. Once you are Neutral with the Scryers, you can no longer receive this quest. The quest gives you +250 Scryers reputation and -275 Aldor reputation (in addition, the quest also gives you +125 reputation with The Sha\'tar).',NULL),(8,935,0,NULL,0,2,'[b]The Sha\'tar[/b], or \"born of light,\" are naaru that aided [faction=932], the order of draenei priests formerly led by [npc=17468], in rebuilding [zone=3703]. The city was destroyed by the Orcs during their rampage across Draenor prior to the First War. Defeat of the Burning Legion is the Sha\'tar\'s ultimate goal; the Sha\'tar are aided in this war by the Aldor and their rivals, the blood elf faction known as [faction=934]. The Aldor and the Scryers fight for the favor of the Sha\'tar so that they may be assisted in their war by the naaru\'s powers. The entity that leads the Sha\'tar is known as [npc=18481]; he can be found upon the Terrace of Light in Shattrath City.\n\nBoth Alliance and Horde players begin as Neutral toward the Sha\'tar. Players can increase their Sha\'tar reputation through various quests, by raising their reputation with the Aldor or Scryers, or by adventuring into [url=?search=Tempest+Keep#z0z]Tempest Keep[/url].\n\n[h3]Reputation[/h3]\n[b]Until Honored[/b]\nReputation can be gained from Scryer/Aldor signet/mark turn-ins. The following will only grant Sha\'tar reputation until you achieve Honored status: [item=29426], [item=30810], and [item=29739] for the Scryers; [item=29425], [item=30809], and [item=29740] for the Aldor. In addition, these will require more turn-ins to produce equable Sha\'tar reputation to the main faction. Note that this reputation gain does not show up in the combat log, but can be verified by looking at your reputation panel.\n\nReputation can also be gained by running Tempest Keep: [zone=3847], [zone=3846] and [zone=3849].\n\n[b]Through Exalted[/b]\nAfter exhausting the reputation rewards from Aldor/Scryer turn-ins and Mechanar runs, players may wish to complete the few Sha\'tar quests available. In addition to the quests, instance runs in Tempest Keep: Botanica, Arcatraz and Mechanar will continue to grant reputation. At this point, it is probably more worthwhile to run these instances in Heroic mode.',NULL),(8,941,0,NULL,0,2,'The [b]Mag\'har[/b] are a faction of brown-skinned orcs who remain on Outland and have separated themselves from the other remaining orc clans that fell prey to [npc=17257] and joined his army of fel orcs (that are now led by the powerful [npc=16808]). The Mag\'har are settled in the stronghold of Garadar in the beautiful land of [zone=3518], once home to the majority of the orcs along with [zone=3519] and the [zone=3522].[pad]The Mag\'har orcs have never been corrupted by Mannoroth or Magtheridon and thus remained untouched by the bloodlust. Unlike their former clanmates who live in the ruins of their once-mighty holds, the Mag\'har are made up of members of different orc clans who escaped corruption. The current leader of the Mag\'har, venerable [npc=18141], is an old and wise orc, yet she has recently fallen extremely ill. [npc=18063], son of the mighty Grom Hellscream, serves as the Mag\'har\'s military chief, aided by [npc=18106], son of the venerable chieftain of the Bleeding Hollow clan, Kilrogg Deadeye. In addition, there is an NPC within a Mag\'har camp to the west known as [npc=18229].[pad]It is not clear how the Mag\'har managed to retain their original brown skin. Orcish skin turns green when exposed to warlock magic, regardless of the individual\'s beliefs or practices; Garrosh and Jorin would certainly have been exposed, given the positions of their fathers. \n\nHorde players start out at unfriendly with the Mag\'har. Alliance players will always be treated as hostile. The Alliance counterpart to this faction are the [faction=978].\n\n[h3]Questing[/h3]\nQuests for the Mag\'har begin in [zone=3483] with [quest=9400] from [faction=947]. This quest will lead you to a small Mag\'har outpost north of Hellfire Citadel. Once in Nagrand, players will find the main Mag\'har city, Garadar. The city holds most of the remaining quests that will reward Mag\'har reputation.\n\nNote: You MUST have completed the quest chain of \"The Assassin\" up until the quest [quest=9410] (where you become Neutral) in order for you to talk to most people in Garadar.\n\n[h3]Reputation[/h3]\nReputation can be gained from killing [url=?npcs&filter=na=kil%27sorrow;ra=-1;rh=-1]Kil\'sorrow cult members[/url], [url=?npcs&filter=na=Murkblood;ra=-1;rh=-1;cr=6;crs=3518;crv=0]Murkblood Broken[/url], [url=?npcs&filter=na=warmaul+-marker]Warmaul[/url] and [url=?npcs&filter=na=boulderfist;minle=64;ra=-1;rh=-1]Boulderfist[/url] ogres in Nagrand. Players may also turn in 10x [item=25433], which drop from these ogres.[pad]Players seeking [faction=933] reputation may wish to save their warbeads, as Mag\'har reputation is generally easier to obtain.[pad]Players seeking [faction=932] reputation may prefer killing cult members at Kil\'Sorrow Fortress, as they drop [item=29425] for Aldor reputation turn-ins.\n\n[i]Note: These monsters and quests do not have a limit, they grant reputation all the way through exalted![/i]',NULL),(8,942,0,NULL,0,2,'Upon the reopening of the Dark Portal to Outland, the [faction=609] dispatched an exploratory force, known as the [b]Cenarion Expedition[/b], to explore the uncharted world. Much like the Circle, it is a coalition of night elf and tauren forces. Since the opening of the Dark Portal, the Cenarion Expedition has quickly gained in size and autonomy, achieving enough power to be considered its own faction. The Expedition maintains its primary base at Cenarion Refuge in [zone=3521]; it has also made its presence known on [zone=3483], in [zone=3519], and in the [zone=3522]. Cenarion Refuge is located immediately west of Thornfang Hill.\n\nThe Refuge is located in the Zangarmarsh for the primary reason of studying the rich wildlife located there. However, the Expedition has discovered troubling goings-on in the marsh. Water levels in many parts of Zangarmarsh are decreasing, and some areas such as the Dead Mire have already suffered greatly from this strange phenomenon. It has become known that this decrease in the water levels can be attributed to pumps that have been constructed in the Marsh by the naga. Their purpose is to create a new Well of Eternity for [npc=22917]. However, the Expedition cannot afford direct confrontation with the naga so numerous in the Zangarmarsh and [url=?search=coilfang#c0z]Coilfang Reservoir[/url]. It needs the aid of those willing to assist the druids in their dangerous battle against those who seek to disturb the marsh\'s natural balance. Quite naturally, those heroic enough to fight the naga at Coilfang Reservoir will be well rewarded.\n\n[h3]Reputation[/h3]\n[b]Neutral to Honored[/b]\nKill Naga, while also running [zone=3717] whenever you can; a good instance run will net reputation faster than soloing. Alternatively, the player can begin turning in [item=24401] for a chance at an [item=24407], which can be turned in for 500 reputation. It is suggested that the player save his Uncatalogued Species until after Honored status is achieved, as the quest cannot be continued past that point, while Uncatalogued Species can be used until Exalted.\n\nIf you are an herbalist, and interested in [faction=970] reputation, you may want to grind the [url=?npcs&filter=na=Bog+Lord]Bog Lords[/url] which can be found in the NE, SE, and SW corners of Zangarmarsh. Their bodies can be \"picked\" by herbalists and often yield Unidentified Plant Parts, while every kill yields 15 reputation with Sporeggar.[pad][b]Honored to Revered[/b]\nOnce the player is Honored, running Slave Pens and the [zone=3716] (with the exception of [npc=17770] and some giants), will no longer grant reputation. You should now do any Cenarion Expedition quests in Hellfire Peninsula, Zangarmarsh, Terokkar Forest and the Blade\'s Edge Mountains. It is also the time to turn in any Uncatalogued Species you have found. Doing this should get you part of the way into Revered.\n\nAlternatively, you can finish leveling to 70 and run [zone=3715]. Each run gives just over 1500 reputation if you clear all mobs. Also within the Steamvault lies a repeatable quest, [quest=9764], which begins with [item=24367]. You will then be able to turn in [item=24368], which drop in both Steamvault and Slave Pens, receiving 250 reputation for the first turn-in and 75 reputation each thereafter. This turn-in is available all the way to Exalted.\n\nOnce you are 70 and have upgraded your gear, you can opt to run Slave Pens, Underbog, and Steamvault on Heroic Mode upon purchasing the [item=30623]. While the instances are difficult, they award significant reputation: regular mobs are worth 15 reputation, 2 for non-elites, and 150/250 for bosses. This method works until Exalted.[pad][b]Revered to Exalted[/b]\nContinue with the same strategy as above: finish any remaining quests, run Steamvault, and continue with [item=24368] turn-ins.\n\nIt is also possible to run Slave Pens, Underbog, and Steamvault on Heroic Mode. The reputation gained is not much more than running Steamvault in normal mode, whilst the time investment for heroic dungeons is much higher, possibly resulting in a lower net reputation per hour, however the loot is better and you will receive [item=29434] from the bosses which can be used to purchase high quality epic gear.',NULL),(8,946,0,NULL,0,2,'A refuge of human, elven, draenei and dwarven explorers, [b]Honor Hold[/b] is the first major town Alliance explorers will encounter while traversing Outland. Vestiges of the Sons of Lothar, veterans of the Alliance that first came into Draenor, have steadfastly held on to this Hellfire outpost. They are now joined by the armies from Stormwind and Ironforge.\n\n[h3]Reputation[/h3]\nHonor Hold reputation is gained through various means in Hellfire Peninsula. Mobs in and around Hellfire Citadel reward Honor Hold reputation, as well as quests picked up in town. Due to the lack of representatives in other areas, there is a large gap between Honored and Exalted during which you may not attain any Honor Hold reputation from questing and killing mobs in Outland once you depart Hellfire Peninsula.\n\n[b]Through friendly[/b]\nMobs in [zone=3562] and [zone=3713] will award reputation through Friendly. One option is to grind reputation via Ramparts and Blood Furnace runs until honored before doing any Honor Hold quests outside the instances, as those continue to yield reputation up to Exalted. You may also want to check out the following outdoor mobs which give reputation if you are Neutral. These mobs will not give reputation once you are Friendly with Honor Hold.[ul][li][npc=19415] [/li][li][npc=16878] [/li][li][npc=16870][/li][li][npc=16867][/li][li][npc=19414] [/li][li][npc=19413] [/li][li][npc=19411] [/li][li][npc=19422][/li][/ul]To make the best use of available resources, you may want to grind reputation with Honor Hold through Hellfire Ramparts and Blood Furnace prior to completing any Honor Hold quests. \n\n[b]PvP[/b]\nPlayers that enjoy PvP can earn Honor Hold reputation through the daily quest [quest=10106]. This quest awards 70 silver and 150 Honor Hold reputation, but can only be completed once a day and counts towards your 25 daily quest limit. Completion of this quest also yields three [span class=q1][item=24579][/span], which are used as currency for various types of items and gear when turned into [npc=17657] and [npc=18266] in Honor Hold as well as the [npc=18581] in Zangarmarsh.\n\n[i]Tip: You can use these marks to purchase [span class=q1][item=24520][/span] from Warrant Officer Tracy Proudwell and increase the amount of reputation (and experience) gained while running these instances.[/i]\n\n[b]Through Exalted[/b]\nFrom here on out there are only two ways to achieve Revered and Exalted status:[ul][li][zone=3714], this instance requires level 68 and the [span class=q1][item=28395][/span] (only one party member needs the key). Mobs in Shattered Halls will yield reputation through Exalted.[/li][li]After achieving Honored status you can purchase the [span class=q1][item=30622][/span] which grants access to the heroic mode of all Hellfire Citadel instances. Mobs in all Heroic mode Hellfire Citadel instances will yield slightly more reputation than those found in non-heroic Shattered Halls, and will continue to yield reputation through Exalted.[/li][/ul]',NULL),(8,947,0,NULL,0,2,'The expedition sent through the Dark Portal by Thrall has built a stronghold in Hellfire Peninsula. [b]Thrallmar[/b] serves as a base of operations for much of the Horde\'s activities in Outland.\n\n[h3]Reputation[/h3]\nReputation for Thrallmar up to Honored is relatively easy to earn. Even the easiest quests (those that take you from one quest giver to the next up the road, for example) can yield 75 reputation points, while those that require some effort to complete typically yield 250 reputation points or more. Some group quests that involve killing an elite can yield as much as 1000 reputation points.\n\nIf you do the bulk of the Thrallmar quests instead of quickly moving on to the next zone, you might expect to reach Honored after 1 or 2 levels of play. However, once you reach Honored, you hit an earnings barrier that you can only remove when you are level 68 and can start re-earning points in the [zone=3714] dungeon.\n\n[b]Neutral through Friendly[/b]\nReputation from mobs in [zone=3562] and [zone=3713] stops at 5999/6000 friendly. One option is to grind reputation via Ramparts and Blood Furnace runs to 5999/6000 before doing any Thrallmar quests outside the instances, as those continue to yield reputation up to Exalted.\n\nAlso, the level 63 mobs outside Hellfire Citadel (on the path) give you 5 reputation each.\n\n[b]Friendly through Honored[/b]\nPlayers that enjoy PvP can earn Thrallmar reputation through the daily quest [quest=10110]. This quest awards 70 silver and 150 Thrallmar reputation, but can only be completed once a day and counts towards your 25 daily quest limit. Completion of this quest also yields three [item=24581], which are used as currency for various types of items and gear when turned into [npc=18267] and the [npc=18564] in Thrallmar and near Zabra\'jin in [zone=3521] respectively.\n\nBlood Furnace and Ramparts instance runs will be your best bet for this reputation bracket. Be aware though, that they will only take you to the end of Honored. You will need to run Shattered Halls to reach Revered status.\n\n[b]Revered to Exalted[/b]\nFrom this point on, gaining reputation through Exalted requires one of two things:[ul][li]Access to Shattered Halls, one of the wings of Hellfire Citadel, which requires level 68 and either the [span class=q1][item=28395][/span] or a rogue with 350 lockpicking skill.[/li][li]Doing Heroic versions of Hellfire Citadel dungeons, which typically require you to be well geared and level 70.[/li][/ul]Both of these give reputation until you reach Exalted status. A full clear of Shattered Halls nets you about 2000 reputation points, trash mobs generally yield 6 or 12 each, with up to 150 points from bosses. Heroic trash yields 15-25 points, with bosses worth more. \n\n[i]Tip: You can purchase [span class=q1][item=24522][/span] from Battlecryer Blackeye for use during instance runs to speed up the reputation (and experience) gaining process![/i]',NULL),(8,967,0,NULL,0,2,'[b]The Violet Eye[/b] is a secret sect founded by the Kirin Tor of Dalaran to spy on the Guardian of Tirisfal, [npc=15608], in his tower of [zone=2562]. Though Medivh is dead, the Violet Eye remains in Karazhan, defending against the evil that appears to have taken hold in the absence of its master. \n\nIt is unknown whether Medivh\'s apprentice, [npc=18166], was a member of the Violet Eye, or whether he knew of their activities at the time (though he does seem to be aware of them now).\n\n[h3]Reputation[/h3]\nViolet Eye reputation is gained by killing mobs inside Karazhan and completing Karazhan related quests. Reputation from Karazhan mobs can be gained from neutral standing all the way to exalted. Each trash mob awards around 15 reputation, with the bosses award more.\n\n[npc=18253] begins a fairly long quest chain starting with [quest=9824] and [quest=9825]. This quest line rewards players with [span class=q1][item=24490][/span] and culminates with [quest=9644]. Full completion of this quest line rewards approximately 10,270 reputation.\n\n[h3]Reputation Rewards[/h3]\n[npc=18253] will offer players rings as rewards for reputation level gains in the form of quests. The first such quest is available at neutral standing and may be completed at friendly. You will receive a new and upgraded version of the ring you chose each time you break into a new reputation tier. The rings are sorted into the following 4 categories:[ul][li][quest=10731]: [item=29280], [item=29281], [item=29282] and [item=29283][/li][li][quest=10729]: [item=29284], [item=29285], [item=29286] and [item=29287][/li][li][quest=10732]: [item=29276], [item=29277], [item=29278], and [item=29279][/li][li][quest=10730]: [item=29288], [item=29289], [item=29291] and [item=29290][/li][/ul][npc=16388], a blacksmith located inside Karazhan just after [npc=15550], offers players with high enough reputation the ability to buy epic blacksmithing plans. Players who are honored or above will also be able to repair armor and weapons at this vendor.\n\n[npc=18255], who stands just outside the main gates of Karazhan, will sell an epic jewelcrafting recipe and shoulder enchant to players who have an honored or above standing with The Violet Eye.',NULL),(8,970,0,NULL,0,2,'The sporelings are a mostly peaceful race of mushroom-men native to Outland. Their home, [b]Sporeggar[/b], is located in the western bogs of [zone=3521].\n\n[h3]Reputation[/h3]\nPlayers both Alliance and Horde start out unfriendly with Sporeggar. There are many ways to increase your reputation at the beginning:[ul][li]Bringing 10 [span class=q1][item=24290][/span] to [npc=17923] to complete [quest=9739][/li][li]Bringing 6 [span class=q1][item=24291][/span] to Fahssn to complete [quest=9743] [i](both of these quests will be available only if you are below friendly)[/i][/li][li]Killing [url=?search=bog+lord+-hungry#z0z]Bog Lords[/url] [i](lasts until the end of honored)[/i][/li][li]Killing [npc=18137] and [npc=18136] [i](lasts until the end of revered)[/i][/li][li]Bringing 10 [span class=q1][item=24245][/span] to [npc=17924] in Sporeggar [i](lasts only during neutral)[/i][/li][/ul]After you hit [b]friendly[/b], a new handful of repeatable quests opens up at the same time Fahssn\'s quests and the Glowcap turnins become unavailable, these include:[ul][li]Killing 12 each of [npc=18088] and [npc=18089] for [npc=17856] to complete [quest=9726][/li][li]Bringing 10 [span class=q1][item=24449][/span] to [npc=17925] to complete [quest=9806][/li][li]Venturing into [zone=3716] to gather 5 [span class=q1][item=24246][/span] for Gzhun\'tt to complete [quest=9715][/li][/ul]These 3 quests are repeatable and will be available to the end of exalted.\n\nPlayers who are exalted with Sporeggar should speak to [npc=17877] for one final quest.',NULL),(8,978,0,NULL,0,2,'Draenei for \"redeemed.\" These Broken have escaped the grasp of their various slavers in Outland and have made their home at Telaar in southern [zone=3518]. It is there that they seek to rediscover their destiny. They also maintain a small presence at Orebor Harborage, [zone=3521]. Their quartermaster, [npc=20240], is located outside the inn in Telaar, below the flight point.\n\nAlliance players start out at unfriendly with the Kurenai. Horde players will always be treated as hostile. The Horde counterpart to this faction are [faction=941].\n\n[i]Kurenai is Japanese for \"crimson\".[/i]\n\n[h3]Gaining Reputation[/h3]\nReputation can be gained from killing [url=?npcs&filter=na=kil%27sorrow;ra=-1;rh=-1]Kil\'sorrow cult members[/url], [url=?npcs&filter=na=Murkblood;ra=-1;rh=-1;cr=6;crs=3518;crv=0]Murkblood Broken[/url], [url=?npcs&filter=na=warmaul+-marker]Warmaul[/url] and [url=?npcs&filter=na=boulderfist;minle=64;ra=-1;rh=-1]Boulderfist[/url] ogres in Nagrand. Players may also turn in [item=25433] (10), which drop from these ogres.\n\nPlayers seeking [faction=933] reputation may wish to save their warbeads, as Kurenai reputation is generally easier to obtain.\n\nPlayers seeking [faction=932] reputation may prefer killing cult members at Kil\'Sorrow Fortress, as they drop [item=29425] for Aldor reputation turn-ins.\n\n[i]Note: These monsters and quests do not have a limit, they grant reputation all the way through exalted![/i]',NULL),(8,989,0,NULL,0,2,'The [b]Keepers of Time[/b] are bronze dragons hand-picked by Nozdormu to watch over the Caverns of Time. They are led by [npc=19932] and [npc=19933], who are also acting leaders of the Bronze Dragonflight in Nozdormu\'s absence.\n\n[h3]Reputation[/h3]\nCurrently the only way to gain the favor of the enigmatic bronze dragons is through [zone=2367] and [zone=2366] instance runs. Keepers of Time reputation rewards may be found at the Keepers\' quartermaster, [npc=21643]. The Keepers will require you to be level 66 and complete the short quest [quest=10277] before allowing passage into Old Hillsbrad Foothills to fulfill [npc=17876]\'s destiny to become the Warchief of the Horde.',NULL),(8,990,0,NULL,0,2,'The [b]Scale of the Sands[/b] is a secretive subgroup of the Bronze Dragonflight, led by [npc=19935], prime mate of [npc=15185]. It is a subgroup of the Bronze Dragonflight. Their leader, Nozdormu, sent these guardian factions to [zone=3606] where they guard the World Tree from another attack by the demons of Darkwhisper Gorge and help restore the time-stream and preserve the future of the world.\n\n[h3]Reputation[/h3]\nBoth bosses and trash monsters give reputation with each kill. [npc=17968], the final boss, awards 1500 reputation while the other four bosses give 375. General trash award 12 reputation, while [npc=17907] give 60. Yielding an average of 7800 per full clear, it would take 5-6 clears to reach exalted.\n\nCurrently some of the best [span class=q4][url=?items=4.-2&filter=na=band+of+the+eternal]rings[/url][/span] for raiding are available via this reputation. In order to recieve the rings, you must complete the previously required attunement quest, [quest=10445]. Each new reputation level awards an upgraded ring.',NULL),(8,1011,0,NULL,0,2,'The [b]Lower City[/b] of [zone=3703] is the place where the refugees gather and help out in their own ways. When someone helps any of the mixture of races who fled from war, word gets around quickly. Their quartermaster, [npc=21655], is located at the market in the Lower City. The Lower City of Shattrath also contains a very useful Mana Loom or an Alchemy Lab. Many NPCs have extensive knowledge of crafting. The Battlemasters for both sides of all four [zones=6] can also be found here, as well as the World\'s End Tavern.\n\nOther important NPCs include:[ul][li]A neutral Grand Master Leatherworker, [npc=19187].[/li][li]A neutral Grand Master Skinner, [npc=19180].[/li][li]A neutral Grand Master Alchemist, [npc=19052], with an Alchemy Lab, who also gives the quest [quest=10902] (for alchemy specialization).[/li][li]Three specialist tailors who allow you to specialize and buy new epic tailoring recipes for armor sets and special bags (including the 20-slot bag).[ul][li][npc=22212] [small][/small] sells the patterns for the [itemset=553] set.[/li][li][npc=22213] [small][/small] sells the patterns for the [itemset=552] set.[/li][li][npc=22208] [small][/small] sells the patterns for the [itemset=554] set.[/li][/ul][/li][/ul]\n\n[h3]Reputation[/h3]\n[b]Until Honored[/b][ul][li]Run [zone=3790] in [i]normal[/i] mode, ~750 reputation.[/li][li]Run [zone=3791] in [i]normal[/i] mode, ~1250 reputation.[/li][li]Run [zone=3789] in [i]normal[/i] mode, ~2000 reputation.[/li][li]Turn in [item=25719] at [npc=22429].[/li][/ul][i]Note: Players aiming for faction higher than Honored should wait until honored to complete the Lower City quests.[/i]\n\n[b]Honored to Revered[/b][ul][li]Run Shadow Labyrinth in [i]normal[/i] mode, ~2000 reputation.[/li][li]Complete all available [url=?quests&filter=cr=1;crs=1011;crv=0]Lower City quests[/url].[/li][/ul][b]Revered to Exalted[/b][ul][li]Run Auchenai Crypts in [i]heroic[/i] mode, ~750 reputation.[/li][li]Run Sethekk Halls in [i]heroic[/i] mode, ~1250 reputation.[/li][li]Run Shadow Labyrinth in [i]normal[/i] or [i]heroic[/i] mode, ~2000 reputation.[/li][/ul]\n\n[h3]Trivia[/h3]\n[npc=19227], a vendor in Lower City, sells amulets which are very... interesting. He is quite the salesman, with items like [item=27940], which allows you to return to life as long as you return to the place you died. [i]Buyer beware![/i]\n\nAt exalted you can purchase a [item=31778]. Strangely, none of the NPCs in Lower City can be seen wearing one. Perhaps they cannot afford one...',NULL),(8,1012,0,NULL,0,2,'The [b]Ashtongue Deathsworn[/b] are the elite of the Broken draenei tribe known as the Ashtongue. The Ashtongue tribe is led by the elder sage [npc=21700]; the Deathsworn are [i]officially[/i] aligned with [npc=22917] [small][/small]. The Deathsworn are Akama\'s most trusted lieutenants and are privy to their leader\'s mysterious motivations.\n\nTo discover the Deathsworn as a faction, the player must begin and complete the majority of the quest line which begins with Tablets of Baa\'ri ([quest=10568] / [quest=10683]). Eventually, you will speak with Akama, whereupon you will become Neutral with the Deathsworn.',NULL),(8,1015,0,NULL,0,2,'The [b]Netherwing[/b] are a faction of dragons located in Outland. The unusual brood was spawned from the eggs of Deathwing\'s black dragonflight, and infused with raw nether-energies. Now, they seek to find their identity beyond the shadows of their father\'s destructive heritage.\n\n[h3]Reputation[/h3]\nPlayers are introduced to the Netherwing faction at 0/36000 hated reputation, and must be exalted to receive a [span class=q4][url=?items=15.-7&filter=na=Netherwing+Drake]Netherwing Drake[/url][/span]. The quest chain and reputation grind is a mostly solo endeavor involving quests that can only be completed once daily, a 5-player group quest on the way to neutral, and daily 3-player group quests after reaching revered. A flying mount is required for this reputation grind, and 300 riding skill is necessary to advance past neutral.\n\n[b]Hated to Neutral[/b]\nLevel 70 players will begin their journey to exalted reputation by picking up the quest chain offered by [npc=22113], a blood elf wandering the surface of the Netherwing Fields, in the southeast corner of [zone=3520]. The quest chain begins with the quest [quest=10804]. Completion of this quest line will provide an instant reputation boost to neutral and the choice of one of [span class=q3][url=?items&filter=qu=3;na=Netherwing+-wand]these[/url][/span] five items.\n\n[h3]Netherwing Reputation After Neutral[/h3]\nAfter completing the Kindness quest chain, Mordenai will be sure you have acquired 300 [spell=34091] skill and have you swear fealty to the Netherwing. This will grant you a Dragonmaw Fel Orc disguise when you enter Netherwing Ledge and allow you to communicate and work for the Dragonmaw stationed there. Mordenai will initially send you to [npc=23139] with a set of fake papers. Completing this quest will unlock the beginning Dragonmaw quests that you\'ll be working on to increase your Netherwing reputation. Most of these quests will have the new \"Daily\" tag added with 2.1. Daily quests differ from regular quests in that they are infinitely repeatable, but you may only complete each daily quest once per day and are restricted to ten total daily quests per day.[pad][i]Note: New quests will be unlocked with each reputation tier, and all daily quests of previous tiers will always be available, even after reaching exalted.[/i]\n\n[b][toggler id=Neutral hidden]Neutral[/toggler][/b]\n[div id=Neutral hidden]After turning in Mordenai\'s [item=32469] to Mor\'ghor to complete [quest=11013], your first group of quests will become available to start you on your way to the next tier of reputation with the Netherwing. Mor\'ghor will point you to the taskmaster to begin your grunt work, and [npc=23141] will reveal himself as a Netherwing ally in disguise and present another group of quests to you. One of which is [quest=11049]. Players will be able to turn in any [item=32506] that have a 1% chance to be found in [object=185881], [object=185877], and on almost all creatures on Netherwing Ledge. It can also be a rare find as a [object=185915] anywhere on Netherwing Ledge and in the Dragonmaw Fortress on the southeast corner of the Shadowmoon Valley mainland. This quest is not labeled as daily, and therefore can be done as many times as you can find eggs and will not hinder your daily quest limit.[pad]Other quests available from the beginning:[ul][li][i][small](Daily)[/small][/i] [quest=11018], [quest=11016], [quest=11017] - These will be available only to players who possess the respective profession to gather each item.[/li][li][i][small](Daily)[/small][/i] [quest=11015] - Simple gathering quest open to all players regardless of profession.[/li][li][i][small](Daily)[/small][/i] [quest=11020] - Yarzill will ask you to collect [item=32502] and use them to poison the peons that are working to gather resources for Dragonmaw.[/li][li][i][small](Daily)[/small][/i] [quest=11035] - You will need to fly to the northeast corner of Netherwing Ledge and position yourself on one of the floating rocks to intercept the [npc=23188] and recover 10 [item=32509].[/li][/ul][/div][pad][b][toggler id=Friendly hidden]Friendly[/toggler][/b]\n[div id=Friendly hidden]Mor\'ghor will award you with an [item=32694] to go with your new rank among the Dragonmaw.[ul][li][quest=11083] - [npc=23166] will task you with quelling the Murkblood Broken that are stationed deeper within the mines.[/li][li][quest=11081] - After finding [item=32726] in a [item=32724], you\'ll begin to reveal what\'s truly happening with the Murkblood in the mine.[/li][li][quest=11054] - [npc=23291] will have you fashion your very own [item=32680] for use in keeping the Dragonmaw peons in line and working at full efficiency.[/li][li][i][small](Daily)[/small][/i] [quest=11076] - The [npc=23149] will ask that you venture into the Netherwing mines and recover the cargo contained in mine carts randomly strewn among the interior of the mine.[/li][li][i][small](Daily)[/small][/i] [npc=23376] - One of the [npc=23376] will inform you that the creatures deeper in the mine are halting production and ask you to thin their numbers.[/li][li][i][small](Daily)[/small][/i] [quest=11055] - This humorous quest starts at Chief Overseer Mudlump after you bring him the required materials. You\'ll be able to fly around Netherwing Ledge and toss the Booterang at any [npc=23311] that can be found anywhere around the crystals of the ledge.[/li][/ul][/div][pad][b][toggler id=Honored hidden]Honored[/toggler][/b]\n[div id=Honored hidden]Mor\'ghor will award you with your new [item=32695], which is now usable anywhere as long as you\'re outside.[ul][li][quest=11063] - This six-part questline will have you in-flight following the other Dragonmaw masters of flight. They will all attempt to knock you off your mount with cleverly-placed air attacks, you must stay within vision range and on your mount until they land or you will fail and need to restart the quest. After defeating the last of the six riders, you\'ll be awarded a [item=32863], which functions exactly like a [item=25653]. The effects of the two trinkets do [b]not[/b] stack.[/li][li][quest=11089] - [npc=23427] will request a set of materials to fashion a special device to destroy his brother and hinder the Legion\'s advances from the Twilight Portal in western [zone=3518].[/li][li][i][small](Daily)[/small][/i] [quest=11086] - Mor\'ghor will send you to the Twilight Portal in Nagrand to kill 20 [url=?npcs&filter=na=deathshadow+-imp+-hound+-agent]Deathshadow Agents[/url]. Beware the overlords, they patrol most of the area and can pack quite a punch.[/li][/ul][/div][pad][b][toggler id=Revered hidden]Revered[/toggler][/b]\n[div id=Revered hidden]Mor\'ghor will award your final trinket upgrade, the [item=32864] after reaching revered.[ul][li]Kill Them All! ([quest=11094]/[quest=11099]) - Mor\'ghor will order you to begin the attack against your chosen faction\'s base of operations in Shadowmoon Valley. Obviously you\'re not going to actually allow the Dragonmaw to attack your allies, so report to the proper leader and unlock your final daily quest for Dragonmaw...[/li][li][i][small](Daily)[/small][/i] The Deadliest Trap Ever Laid ([quest=11097]/[quest=11101]) - Waves of Dragonmaw Skybreakers will attack after preparations are made. Bring allies, as this is a battle of attrition.[/li][/ul][/div][pad][b][toggler id=Exalted hidden]Exalted[/toggler][/b]\n[div id=Exalted hidden]After many days of work, finally the denouement of the Netherwing/Dragonmaw questline. Taskmaster Varkule will direct you to Mor\'ghor one last time, who will inform you that you will be promoted by [npc=22917] himself. Without spoiling the events that ensue, you will end up in Shattrath with your selection of Netherdrake epic mounts. You may choose one here for free, and if you decide on a different color later, you can speak with [npc=23489] back in the Dragonmaw Base Camp to buy another drake for 200 gold.[/div]',NULL),(8,1031,0,NULL,0,2,'The [b]Sha\'tari Skyguard[/b] are an air wing of the [faction=935] of [zone=3703], defending the capital from attackers in the hills as well as battling against the arakkoa of Terokk in the peaks of Skettis. The Skyguard has two outposts, one in the northern reaches of the Skethyl Mountains and one near [faction=1038]. Players start out at neutral standing with the Skyguard.\n\n[h3]Reputation[/h3]\n[b]Daily Quests[/b][ul][li][quest=11008] - [npc=23048] will grant you a pack of explosives to destroy the eggs that rest atop Skettis structures.[/li][li][quest=11085] - A [npc=23383] can be found atop certain structures, players will escort him out for reputation, gold, and a choice of either 2 [item=28100] or 2 [item=28101].[/li][li][quest=11065] - [npc=23335] will inform you that the Skyguard\'s bombing runs have taken a toll on their mounts and ask you to gather some more Aether Rays to supplement their scout force.[/li][li][quest=11010] - [npc=23120] asks you to destroy the ammo for the Legion\'s flak cannons so the Skyguard Scouts can continue their job.[/li][li][quest=11004] - After collecting 6 [item=32388], [npc=23042] will make a potion that will allow vision of the more powerful arakkoa, such as [npc=23066].\n[i][small]Note: World of Shadows is not a daily quest, but may be repeated as many times as necessary.[/small][/i][/li][/ul][b]Creatures[/b][ul][li][npc=21804] - 5 reputation, up to the end of Revered.[/li][li][url=?npcs&filter=na=skettis+-kaliri+-assassin;minle=70]All Skettis Arakkoa[/url] - 10 reputation, regardless of Skyguard standing.[/li][li][npc=23029] - 30 reputation, regardless of Skyguard standing.[/li][/ul]',NULL),(8,1038,0,NULL,0,2,'The [b]Ogri\'la[/b] are a faction of ogres in the [zone=3522], where their proximity to [item=32572] has allowed them to evolve past their brutish nature. They are currently fighting a war against both the Black Dragonflight and the Burning Legion, who seek the Apexis Crystals for their own purposes.\n\n[h3]Location[/h3]\nOgri\'la is situated near the western edge of Blade\'s Edge Mountains, between Forge Camp: Terror and Forge Camp: Wrath, just west of Sylvanaar. Ogri\'la is only accessible by flying mount/form. Another alternative is to have a reputation of honored or higher with [faction=1031]. But a player must have a flying mount to reach the Skyguard camp near Skettis.[pad]\n\n[h3]Reputation[/h3]\nReputation with Ogri\'la can only be gained via Quests, and there only repeatable quests are the available [url=?quests&filter=da=ja;cr=1;crs=1038;crv=0]daily quests[/url]. Thus, there is a cap on how much reputation a day a player can gain reputation with Ogri\'la, making it an \"ungrindable\" reputation.\n\n[b]Apexis Shards[/b]\n[item=32569] can be collected in a variety of ways. They can be looted from mobs, gathered from the environment, or they can be rewards from completed quests.[pad][b]Apexis Crystals[/b]\n[item=32572] are dropped from elite demons and dragons in Blade\'s Edge Mountains. In order to summon these mobs, 35 Apexis Shards are needed, and it is recommended that you have a 5 man group to defeat them.\n\n[b]Quests[/b]\nThere are a [url=?quests&filter=cr=1;crs=1038;crv=0]number of quests[/url] that a player can to do earn reputation with the Ogri\'la, as well as several [url=?quests&filter=da=ja;cr=1;crs=1038;crv=0]daily quests[/url]. Many of the daily quests will also grant reputation with the Sha\'tari Skyguard when they are first completed. \n\nIn order to access the main quests at Ogri\'la itself, a player must first complete the 5 group quests from [npc=22941].\n\n[h3]Depleted Items[/h3]\nA number of \"depleted\" items will sometimes drop from mobs. When combined with 50 Apexis Shards, the items [url=?search=Apexis+Crystal+Infusion]upgrade[/url], gaining stats and gem slots. Once the items are upgraded they become Bind on Equip, and can therefore be sold or traded to other players. One thing to note, however, is that although the depleted items may also have stats or effects, they cannot be equipped.',NULL),(NULL,NULL,0,'sound&playlist',0,2,'Here you can set up a playlist of sounds and music. \n\nJust click the \"Add\" button near an audio control, then return to this page to listen to the list you\'ve created.',NULL),(14,11,2,NULL,0,2,'[b]Aperçu :[/b] Les [b]Draenei[/b] sont des adeptes de Naaru et adorateurs de la Lumière Sainte. Chassées d’Argus, leur monde natal, les honorables Draeneï durent fuir des siècles durant Sargeras et sa Légion Ardente, après qu’il ait essayé de les corrompre. Les Draeneï ont alors trouvé une lointaine planète où s’établir. Ils appelèrent Draenor ce monde qu’ils partageaient avec les Orcs chamaniques. Une période de paix s’est alors installée.\nLa Légion Ardente fini par retrouver les DraeneÏ et corrompt les Orcs grâce à Guldan. Les Orcs partirent en guerre et exterminèrent les paisibles Draeneï. De rares survivants purent s’enfuir en Azeroth pour chercher de l’aide dans leur combat contre la Légion Ardente.\n\n[b]Capitale :[/b] Les Draeneï ont le siège de leur pouvoir dans les ruines de leur vaisseau : [zone=3557].\n\n[b]Zone de départ :[/b] [zone=3524] et [zone=3525] couvrent les tentatives des Draeneï de s’installer sur leurs nouvelles îles et de faire face à la corruption présente.\n\n[b]Montures :[/b] [npc=17584] vend des variétés d’Elekks, ainsi que [npc=33657] au tournoi d’Argent.',NULL),(14,8,2,NULL,0,2,'[b]Aperçu :[/b] Les [b]Trolls[/b] Sombrelance vécurent à l\'origine dans les îles Brisées mais furent envahis par les nagas et les murlocs. Chassés de chez eux, la [url=?faction=530]tribu de Sombrelance[/url] se lie finalement d\'amitié avec les orcs qui ont sauvés les Trolls de la destruction. [npc=4949] leur offre l\'amnistie parmi la Horde, en contrepartie, la tribu Sombrelance jura fidélité au chef de guerre orque.\nBien qu\'ils refusent d\'abandonner leur sombre héritage, les féroces Trolls Sombrelance occupent une place d\'honneur au sein de la Horde.\n\n[b]Capitale :[/b] Les Trolls Sombrelance vivent maintenant dans la capitale de la Horde : [zone=1637].\n\n[b]Zone de départ :[/b] Les Trolls commencent leurs quêtes en [zone=14]\n\n[b]Montures :[/b] [npc=7952] au village de Sen\'jin vend de nombreux raptors ; [npc=33554], au tournoi d\'Argent, vend quelques modèles distincts.',NULL),(14,10,2,NULL,0,2,'[b]Aperçu :[/b] Les [b]Hauts-Elfes[/b], race fière et hautaine, fondèrent jadis Quel’Thalas où ils créèrent une fontaine magique appelée Puits de Soleil. Ils profitèrent de sa puissance mais devinrent peu à peu dépendants de la magie. Si celle-ci devait être enlevée, les Hauts-Elfes soufreraient horriblement. Ils se séparèrent donc du reste de la société elfique.\nDe nombreux siècles plus tard, le fléau mort-vivant détruisit le Puit de Soleil et tua la plupart des Hauts-Elfes. Les survivants de l’assaut d’Arthas sur Lune-d’Argent, qui ont alors pris le nom d’Elfes de Sang, rebâtissent Quel’Thalas et cherchent de nouvelles sources de magie pour calmer leur douloureux manque.\nLes Elfes de Sang rejoignent la Horde à Burning Crusade.\n\n[b]Capitale :[/b] Les Elfes de Sang ont reconstruit [zone=3487].\n\n[b]Zone de départ :[/b] Les Elfes de Sang commencent au [zone=3430].\n\n[b]Montures :[/b] [npc=16264], aux Bois des Chants Eternelles, vend de nombreux faucons pèlerins ; [npc=33557], au tournoi d’Argent, vend quelques modèles uniques.',NULL),(14,7,2,NULL,0,2,'[b]Aperçu :[/b] Les [b]Gnomes[/b], race excentrique, sont obsédés par les gadgets et la technologie. Malgré leur petite taille, ils ont mis à profit leur grande intelligence pour s\'assurer une place dans l\'Histoire.\nA l\'origine, les Gnomes viennent de la ville de [zone=721], qui était autrefois une merveille technologique mue à la vapeur. Malheureusement, la ville a été détruite par [npc=7937] à la suite d\'une tentative pour sauver la ville d\'une armée massive de Troggs.\nSes bâtisseurs sont désormais des vagabonds qui errent sur les terres des nains, venant en aide à leurs alliés du mieux qu\'il le peuvent.\n\n[b]Capitale :[/b] Aujourd\'hui, les Gnomes font leurs maisons à [zone=1537] malgré les efforts fournis pour reprendre leur bien aimée ancienne ville avec l\'[achievement=4786].\n\n[b]Zone de départ :[/b] Les Gnomes commencent à [zone=1], mais ont une séquence de quêtes très différente des Nains, couvrant Gnomeregan\n\n[b]Montures :[/b] [npc=7955] à Dun Morogh vend de nombreux mécanotrotteurs, ainsi que [npc=33650] au tournoi d\'Argent.',NULL),(14,6,2,NULL,0,2,'[b]Aperçu :[/b] Les [b]Taurens[/b], race aux racines chamaniques profondes, sont des résidents de longue date de Kalimdor. Ils vouent un amour profond et durable à la nature, la grande majorité d’entre eux adorent une divinité connue sous le nom de la Terre Mère.\nRécemment attaqués par des centaures, les Taurens auraient été exterminés s’ils n’avaient pas rencontré, par hasard, les Orcs qui les aidèrent à repousser leurs ennemis. Afin d’honorer cette dette de sang, les Taurens ont rejoint la Horde, renforçant ainsi l’amitié entre les deux races.\n\n[b]Capitale :[/b] [zone=1638] est le lieu de résidence des Taurens\n\n[b]Zone de départ :[/b] Les Taurens commencent leurs quêtes en [zone=215].\n\n[b]Montures :[/b] [npc=3685] vend de nombreux kodos ; [npc=33556], au tournoi d’Argent, vend quelques modèles distinctifs.',NULL),(14,5,2,NULL,0,2,'[b]Aperçu :[/b] Les [b]Réprouvés[/b], résultat d’une première attaque du Fléau en Azeroth, sont une métamorphose d’un certain nombre de membres de l’Alliance en mort vivant. Quand les forces combinées des Orcs, des Elfes, des Trolls, des Nains et des Humains se mirent à se défendre, [npc=36597] se mit à affaiblir ses armées en perdant le contrôle de certaines. Libérés de l’emprise du Roi Liche ainsi que des émotions gênantes et des liens de leurs vies humaines, les Réprouvés, menés par la banshee Sylvanas, réclament vengeance contre le fléau.\nLes Humain sont également devenus des ennemis, impitoyables dans leur désir de purger les terres de tous les mort-vivants. \nLes Réprouvés ne se soucient que très peu de leurs alliés. La Horde ne représente à leurs yeux qu’un simple outil qui pourrait servir leurs sombres desseins.\n\n[b]Capitale :[/b] Les Réprouvés résident sous les ruines de l’ancienne ville humaine de Lordaeron : la [zone=1497].\n\n[b]Zone de départ :[/b] Tous les joueurs de Réprouvés commencent dans la [zone=85]. Ils sont élevés par les Val’kyrs comme des réprouvés de seconde génération\n\n[b]Montures :[/b] [npc=4731] vend de nombreux chevaux mort-vivants ; [npc=33555], au tournoi d’Argent, vend quelques modèles distincts.',NULL),(14,4,2,NULL,0,2,'[b]Aperçu :[/b] Les [b]Elfes de la nuit[/b], race ancienne et mystérieuse, vivaient à Kalimdor pendant des milliers d\'années, ils fondèrent un vaste empire, mais leur usage imprudent de la magie les conduisit à leur perte. Pétris de douleur, ils se retirèrent dans les forêts et demeurèrent ainsi isolés jusqu\'au retour de leur ancien ennemi. Ne disposant d\'aucune alternative, les Elfes de la nuit furent contraints de sacrifié l\'arbre monde afin d\'arrêter l\'avancé de la Légion Ardente. \nIls émergèrent de leur isolement, afin de défendre leur place dans le nouveau monde.\n\n[b]Capitale :[/b] La capitale des Elfes de la nuit est [zone=1657], située dans les branches de l\'arbre monde.\n\n[b]Zone de départ :[/b] Les Elfes de la nuit commencent à [zone=141]\n\n[b]Montures :[/b] [npc=4730], à Darnassus, vent une variété de sabre de nuit, ainsi que [npc=33653] au tournoi d\'Argent.',NULL),(14,3,2,NULL,0,2,'[b]Aperçu :[/b] Les [b]Nains[/b], race robuste, viennent de Khaz Modan dans les Royaumes de l’Est. Par la passé, les Nains ne s’intéressaient qu’aux richesses extraites des profondeurs de la terre. Lorsque des études semblèrent indiquer que les Nains étaient les descendants d’une race proche des Titans qui leur aurait conféré un héritage enchanté, la curiosité des Nains fut piquée au vif. Décidés à en savoir plus, les Nains commencèrent à rechercher des artefacts perdus et des connaissances disparues. Aujourd’hui, les Nains dirigent des fouilles archéologiques partout dans le monde.\nTrois principaux Clans de Nains sont répartis dans tout Azeroth : Les Barbes de Bronze, Les Marteaux Hardis et les Sombrefers.\n\n[b]Capitale :[/b] Les Nains font leur maison dans leur siège ancestral de [zone=1537].\n\n[b]Zone de départ :[/b] Les Nains commencent à [zone=1].\n\n[b]Montures :[/b] [npc=1261] vend des béliers à la ferme des Amberstill, ainsi que [npc=33310] au tournoi d’Argent.',NULL),(14,1,2,NULL,0,2,'[b]Aperçu :[/b] Les [b]Humains[/b], race la plus jeune et la plus peuplés d\'Azeroth, maîtrisent les arts du combat, l\'artisanat et la magie avec une efficacité stupéfiante. La valeur et l\'optimisme des Humains les ont conduits à bâtir certains des plus grands royaumes du monde. En cette ère de troubles, après des générations de conflit, l\'Humanité aspire à ranimer sa gloire passée et à se forger un nouvel avenir rayonnant.\nLes Humains, aux talents très variés, sont devenus les chefs de l\'Alliance grâce à leurs ambitions et leurs résiliences. \n \n[b]Capitale :[/b] Le siège du pouvoir Humain est dans la ville reconstruite de [zone=1519].\n \n[b]Zone de départ :[/b] Les Humains commencent leurs quêtes dans la [zone=12].\n \n[b]Montures :[/b] [npc=384] vend des palefrois dans Hurlevent, et [npc=33307], au tournoi d’Argent, vend quelques modèles distincts.',NULL),(14,2,2,NULL,0,2,'[b]Aperçu :[/b] Les [b]Orcs[/b] étaient, à l\'origine, un peuple pacifique aux croyances chamaniques résidant sur le monde de Draenor. Malheureusement, infectés par le sang démoniaque de Mannoroth le destructeur, les Orcs furent réduit en esclavage par la Légion Ardente, contraint de guerroyer contre les Draenei et de conquérir Azeroth. \nAprès de nombreuse années de joug, les Orcs ont réussi à se libérer de l\'emprise démoniaque et ont conquis leur liberté, pour revenir à leurs racines chamaniques.\nMaintenant, sous la direction de leur nouveau chef de guerre, les Orcs se construisent un nouveau foyer, où ils combattent pour l\'honneur, dans un monde étranger, haïs et calomniés.\n\n[b]Capitale :[/b] Les Orcs résident maintenant dans la ville d\'[zone=1637], du nom du défunt Orgrim Doomhammer, ancien chef de guerre de la Horde.\n\n[b]Zone de départ :[/b] Les Orcs commencent leurs quêtes en [zone=14].\n\n[b]Montures :[/b] [npc=3362], à Orgrimmar, vend une variété de loups ; [npc=33553], au tournoi d\'Argent, vend quelques montures distinctives',NULL),(NULL,NULL,0,'reputation',0,2,'[b]Reputation[/b] is a rough measurement of how much you participate in the community--it is earned by convincing your peers that you know what you’re talking about. Our community puts just as much work as our developers do into making our site as awesome as it is and reputation is meant as a way for you to track just how much work you\'re putting into us.\r\n\r\nThe primary means of gaining reputation is by posting quality comments on database entries (which are then voted up by other site members) and by general contributions to the site which can include actions like data and screenshot submissions. Whenever you leave a comment on a database entry, your peers can then vote on these comments, and those votes will cause you to gain reputation. You can also earn reputation by voting on other users\' comments and by sending in reports!\r\n\r\nBy being a good-standing and contributing user you will be able to earn both reputation and achievements for many of the same actions!\r\n\r\n[h3]Reputation Gains[/h3]\r\n[div style=\"max-width:400px\"][table class=grid]\r\n[tr][td][url=?account=signup]Registering[/url] an account[/td]\r\n[td align=right class=no-wrap]CFG_REP_REWARD_REGISTER reputation[/td]\r\n[/tr]\r\n[tr][td]Daily visit[/td]\r\n[td align=right class=no-wrap]CFG_REP_REWARD_DAILYVISIT reputation[/td]\r\n[/tr]\r\n[tr][td]Posting a comment[/td]\r\n[td align=right class=no-wrap]CFG_REP_REWARD_COMMENT reputation[/td]\r\n[/tr]\r\n[tr][td]Your comment was voted up (each upvote)[/td]\r\n[td align=right class=no-wrap]CFG_REP_REWARD_UPVOTED reputation[/td]\r\n[/tr]\r\n[tr][td]Submitting a screenshot[/td]\r\n[td align=right class=no-wrap]CFG_REP_REWARD_UPLOAD reputation[/td]\r\n[/tr]\r\n[tr][td]Submitting a guide (approved)[/td]\r\n[td align=right class=no-wrap]CFG_REP_REWARD_ARTICLE reputation[/td]\r\n[/tr]\r\n[tr][td]Filing a report (accepted)[/td]\r\n[td align=right class=no-wrap]CFG_REP_REWARD_GOOD_REPORT reputation[/td]\r\n[/tr]\r\n[/table][/div]\r\n\r\n\r\n[h3]Site Privileges[/h3]\r\nThe higher your reputation level, the more privileges you gain. Earn a high enough reputation to unlock additional rewards, in the form of new privileges around the site!\r\n[pad]\r\n[div style=\"max-width:400px\"][table class=grid]\r\n[tr][td]Post comments[/td]\r\n[td align=right class=no-wrap]CFG_REP_REQ_COMMENT reputation[/td]\r\n[/tr]\r\n[tr][td]Upvote on comments[/td]\r\n[td align=right class=no-wrap]CFG_REP_REQ_UPVOTE reputation[/td]\r\n[/tr]\r\n[tr][td]Downvote on comments[/td]\r\n[td align=right class=no-wrap]CFG_REP_REQ_DOWNVOTE reputation[/td]\r\n[/tr]\r\n[tr][td]More votes per day[/td]\r\n[td align=right class=no-wrap]CFG_REP_REQ_VOTEMORE_BASE reputation[/td]\r\n[/tr]\r\n[tr][td]Comment votes worth more[/td]\r\n[td align=right class=no-wrap]CFG_REP_REQ_SUPERVOTE reputation[/td]\r\n[/tr]\r\n[/table][/div]\r\n[pad]\r\n[url=?privileges]Check out full details on site privileges you can earn![/url]\r\n',NULL),(NULL,NULL,0,'privilege=1',0,2,'[h3]Reputation required for posting comments?[/h3]\nThe very first privilege you can earn is the ability to post comments. Because this privilege requires only CFG_REP_REQ_COMMENT reputation, it is earned soon upon registering an account (which awards CFG_REP_REWARD_REGISTER reputation)! Keep this in mind if you\'ve recently registered to post on a contest thread.\n\n[h3]How do I post a comment?[/h3]\nOnce you have earned the ability to post comments, it\'s easy to do! Got some interesting information about an item? Strategies for earning an achievement or killing a boss? These are just a few examples of what could make a quality comment here!\n\nSimply visit any database page that you wish to leave a comment on and scroll down to the \'Contribute\' section. In the \'Add your comment\' tab, you can easily write and format your database comment. You can use our handy formatting buttons to improve the visual quality of your post, and easily add database links using the \'Links\' menu and entering database entry IDs. Once you\'re done, simply click the \'Submit\' button below and voila!\n\n[h3]Comment rating and you![/h3]\nAll comments made on database pages are subject to our rating system. This allows users who have reached the appropriate reputation level to upvote and downvote comments based on their quality. Making quality comments will earn you website reputation each time it has been upvoted, but make a poor quality comment and you may end up losing reputation if it is downvoted!\n\nFor more information on commenting, be sure to check out our handy [url=?help=commenting-and-you]Commenting and You[/url] guide in the website help section!',NULL),(NULL,NULL,0,'privilege=2',0,2,'[h3]Posting External Links[/h3]\nOne of the first privileges allowed to users is the ability to post external links on the site. This will allow you to link to relevant information found on other websites from our database as well as in our forums. You can also add a link to your user profile, such as to your guild website or personal blog. Users without the appropriate reputation level will have their links filtered automatically, to help prevent spammers and malicious links from being posted on our website.\n\n[h3]Posting Policy[/h3]\nPlease be aware that some URLs may still be filtered out by our moderation team, as they made be deemed inappropriate or advertising. If you are uncertain whether or not a link will be considered advertisement, please do not hesitate to contact our Feedback team with any questions!\n',NULL),(NULL,NULL,0,'privilege=4',0,2,'[h3]No CAPTCHAs[/h3]\nAh, CAPTCHAS. Love \'em or hate \'em, they\'re often a necessary evil for popular websites which allow any sort of user contribution. Here, we use [url=https://www.google.com/recaptcha/intro/index.html]ReCAPTCHA[/url] which helps thwart bots and spammers from abusing our forum and comment systems. Unfortunately, this also creates a minor inconvenience for our more active users, who are still occasionally asked to input a CAPTCHA despite long since establishing themselves as a legitimate member of the community. Well, not anymore! Users who reach the appropriate reputation level will no longer have to enter CAPTCHAs anywhere on the site!\n',NULL),(NULL,NULL,0,'privilege=5',0,2,'[h3]Comment rating value increase[/h3]\nWhen you have reached a higher reputation level, your contributions to the site will raise in value! As a more trusted member of our community, your comment ratings will now have an increased weight and, as a result, have a greater effect on the total rating of a comment! Your vote contribution are doubled, so each of upvote will count as two votes (and each of your downvotes as two, as well)! This will allow higher reputation users to have more of an effect on considering quality of a comment, raising quality comments higher and lowering poor comments faster.\n',NULL),(NULL,NULL,0,'privilege=9',0,2,'[h3]More votes per day[/h3]\nWe have a daily cap for comment votes set to CFG_USER_MAX_VOTES.\n\nThis privilege instantly increases the cap by 1, and then increases the cap by an additional 1 point for each CFG_REP_REQ_VOTEMORE_ADD reputation you have above CFG_REP_REQ_VOTEMORE_BASE.\n',NULL),(NULL,NULL,0,'privilege=10',0,2,'[h3]Upvoting Comments[/h3]\nDid you find a comment particularly insightful or laugh out loud funny? Upvote it then! Upvoting is a way of giving props to those who truly contribute. From small guides to witty jokes, if a comment has enhanced your user experience, you should remember to upvote it.\n\nThe higher amount of upvotes a comment has, the higher up on the page it is. This way the community can help determine what comments are worth reading by sending some upvotes their way.\n\n[h3]Upvoting Policy[/h3]\nYou should not use upvotes to reward your friends or withhold upvotes to punish users you dislike. These are bannable offenses and you will probably lose your ability to upvote if we catch you doing it.\n',NULL),(NULL,NULL,0,'privilege=11',0,2,'[h3]Downvoting Comments[/h3]\nDid you find a comment that was out of date, irrelevant, or otherwise less than useful? Downvote it then! Downvoting is a way of removing the clutter from the database and ensuring our comments are up to date. Downvotes remove an upvote--and if a comment has too many downvotes, it can even become a negative comment which appear at the end of an article rather than the beginning. \n\n[h3]Downvoting Policy[/h3]\nYou should not use downvotes to punish users you dislike nor should you downvote in quick succession. Try to use downvotes only to help us out, leaving personal bias out of it. If you abuse downvotes either by making too many in a short time frame or targeting a specific user, you may be warned and in some cases banned.\n',NULL),(NULL,NULL,0,'privilege=12',0,2,'[h3]Replying to a Comment[/h3]\nYou can reply to comments easily and quickly with the new commenting system. All you have to do is leave a reply on an existing comment for this to work.\n\nA reply is best used to illustrate alternatives to a comment, highlight its accuracy, or expand on a joke. For example, if someone says an item drops from a certain boss but you know it does not, you could reply to explain it doesn\'t; it\'s likely people will find your comment helpful so they don\'t waste time trying to get the item from that NPC.\n\nPlease be aware that you should not use comments like forum threads for discussion.\n',NULL),(NULL,NULL,0,'privilege=13',0,2,'Your avatar on the [url=CFG_BOARD_URL]Forums[/url] has an uncommon-quality green border.',NULL),(NULL,NULL,0,'privilege=14',0,2,'Your avatar on the [url=CFG_BOARD_URL]Forums[/url] has a rare-quality blue border.',NULL),(NULL,NULL,0,'privilege=15',0,2,'Your avatar on the [url=CFG_BOARD_URL]Forums[/url] has an epic-quality purple border.',NULL),(NULL,NULL,0,'privilege=16',0,2,'Your avatar on the [url=CFG_BOARD_URL]Forums[/url] has a legendary-quality orange border.',NULL),(NULL,NULL,0,'privilege=17',0,2,'[img src=STATIC_URL/images/premium/user-badge.png border=0 float=right]Unlock [url=HOST_URL/?premium]AoWoW Premium[/url] status for free.\n\nAs a Premium user, you can access a variety of perks:\n[ul]\n[li]Images in tooltips[/li]\n[li]Additional avatar borders[/li]\n[li]And much more![/li][/ul]\n\n',NULL),(13,1,2,NULL,0,2,'[b][color=c1]Les Guerriers[/color][/b] sont une classe très puissante, avec la capacité de taner ou d\'infliger des dégâts de mêlée. Sa caractéristique principale est la force, mais les tanks s\'intéresseront également à l\'Endurance.\n\nCe combattant se bat avec une posture ce qui lui permet l\'accès à différentes capacités et lui accorde des bonus. Il utilisera [spell=71] pour tanker (appris au niveau 10) et [spell=2457] (appris au niveau 1) ou [spell=2458] (appris au niveau 30) pour les dégâts en mêlée.\n\nL\'arbre de protection du Guerrier contient de nombreux talents pour améliorer leur survie et générer des menaces contre les monstres. Les Guerriers de protection sont l\'une des principales classes de tank du jeu. Pour aller au combat, ils peuvent utiliser [spell=100] ou [spell=20252] mais seul le Guerrier protection peut protéger un allié en utilisant [spell=3411].\nIls ont également deux arbres de talent orientés sur les dégâts [icon name=ability_rogue_eviscerate][url=spells=7.1.26]Armes[/url][/icon] et [icon name=ability_warrior_innerrage][url=spells=7.1.256]Fury[/url][/icon], ce dernier comprend le talent [spell=46917], qui permet au Guerrier de manier deux armes à deux mains. Les Guerriers sont capable de faire de gros dégâts de zone avec des sorts tels que [spell=845], [spell=1680] et [spell=46924]. \n\nLe Guerrier porte une armure en plaques et aspire à la perfection dans les combats. Lorsqu\'il inflige ou subit des dégâts, il génère de la rage, utilisée pour alimenter ses attaques spéciales.\n[ul]\n[li] Allié utile, qui peut ajouter des buffs au groupe ou raid avec [spell=6673] et [spell=469], mais seul les Guerriers Fury peuvent fournir un buff passif [spell=29801] qui augmente les coups critiques en mêlée et à distance.[/li]\n[li] L\'avantages uniques des Guerriers, ce sont les 3 postures de combats.[/li]\n[li] Il peut choisir de se spécialiser dans le port d’armes à deux mains, d\'arme à une main, ou dans l\'utilisation du bouclier en plus d\'une arme à une main.[/li]\n[li] Et dispose de plusieurs techniques qui permettent de se déplacer rapidement sur le champ de bataille.[/li]\n[/ul]',NULL),(13,2,2,NULL,0,2,'[b][color=c2]Les Paladins[/color][/b] sont des combattants qui utilisent la magie du sacré pour soigner les blessures et combattre le mal. Ils sont relativement autonomes et disposent de nombreuses techniques destinées à empêcher les morts. Le paladin peut choisir de se battre, de protégés ou de soigner, il utilisera le mana pour combattre le mal. Ses caractéristiques principales dépendent du rôle choisi.\n\nIl est un mélange d’un combattant en mêlée et d’un lanceur de sorts secondaires. Allié indispensable dans un combat, il renforce leurs amis avec de saintes auras (une aura active par paladin sur chaque membre du raid) et des bénédictions spécifiques pour les protéger du mal et renforcer leurs pouvoirs.\n\nPortant de lourdes armures, ils peuvent résister à des coups terribles dans les batailles les plus dures tout en guérissant leurs alliés blessés et en ressuscitant les morts. Au combat, ils peuvent utiliser des armes à deux mains, paralyser leurs ennemis, détruire des morts vivants et des démons, et les juger avec une sainte vengeance.\nLes paladins sont une classe défensive, principalement conçus pour survivre à leurs adversaires, grâce à leur assortiment de capacités défensives. Ils font aussi d’excellents tanks en utilisant leurs capacités [spell=25780].\n\n[ul]\n[li] Classe pouvant guérir, tanker avec leur précieux bouclier et infliger des dégâts en mêlée.[/li]\n[li] Renforce les alliées avec les [url=spells=7.2&filter=na=aura]Auras[/url], les [url=spells=7.2&filter=na=bénédiction]bénédictions[/url] et d’autres buffs.[/li]\n[li] Seule classe avec un véritable sort d’invulnérabilité [spell=642].[/li]\n[/ul]\n\n[b]Montures de classe :[/b]\n[ul]\n[li] [spell=13819] est un destrier royal que seuls les plus fervents des paladins peuvent appeler à leur service. Niveau 20 - Bonus de Vitesse de 60%. [/li]\n[li] [spell=23214] est un équipier infatigable capable d\'amener son valeureux maître dans tout Azeroth. Niveau 40 - Bonus de vitesse de 100%. [/li]\n[/ul]',NULL),(13,4,2,NULL,0,2,'[b][color=c4]Les Voleurs[/color][/b] sont une classe de mêlée capable d\'infliger de grandes quantités de dégâts à leurs ennemis avec des attaques rapides en utilisant de l\'énergie comme ressources. Leurs caractéristiques principales sont la puissance d\'attaque et l\'agilité.\n\nLes Voleurs ont un puissant arsenal de compétences, dont beaucoup sont renforcés par leur capacité de furtivité et d\'étourdissement de leurs victimes. Capables d\'utiliser des poisons, ils paralysent leurs adversaires, les affaiblissant massivement dans la bataille. Avec l\'ambidextrie, ils peuvent utiliser une large gamme d\'armes, mais les Voleurs privilégient la dague, qui est la plus représentative de cette classe. \n\nCe sont les maîtres pour se déplacer furtivement autour de leurs ennemis, frapper dans l\'ombre un adversaire pour tenter de l\'achever rapidement puis s\'échapper du combat en un clin d’œil. \nIls endossent donc souvent le rôle d\'assassin ou d\'éclaireur, mais nombre d\'entre eux sont des loups solitaires.\n\n[ul]\n[li] Porte des armures en cuir.[/li]\n[li] Porte une arme dans chaque main.[/li]\n[li] Utilise une grand variété d\'armes de mêlée, comme les poignards, les armes de pugilats, les masses à une main, les épées à une main et les haches à une main.[/li]\n[li] Recouvre leurs armes avec du [url=items=0.-3&filter=na=poison;ub=4]poison[/url] pour gravement affaiblir leurs ennemis.[/li]\n[li] Utilise le [spell=1784] pour n’être visible que par les ennemis les plus perspicaces.[/li]\n[li] Cumule 5 points de combo pour infliger de puissants coups de grâce.[/li]\n[/ul]',NULL),(13,3,2,NULL,0,2,'[b][color=c3]Les Chasseurs[/color][/b] sont une classe très unique dans le monde de World of Warcraft. C\'est la seule classe non-magique qui fait des dégâts à distance. Ils se battent avec des arcs, des armes à feu ou des arbalètes. Leurs caractéristiques principales sont la puissance d\'attaque et l\'agilité.\n\nLes Chasseurs se sentent chez eux dans la natures et ont une affinité spéciale avec les animaux. Il sait apprivoiser son propre [url=pets]familier[/url] qui l\'aidera à vaincre son ennemi. L\'animal du chasseur est unique, il possède un arbre de talent où le Chasseur peut attribuer des points dans des compétences diverses et des capacités passives. Chaques espèces de familier a une capacité spéciale unique. Le Chasseur peut rechercher les bêtes les plus appréciables en fonction de leurs apparences ou capacités. Seuls certains familiers ne sont accessibles que si le Chasseur choisi dans son arbre de talent [icon name=ability_hunter_beasttaming][url=spells=7.3.50]Maîtrise des bêtes[/url][/icon] qui lui donne accès aux bêtes « exotique » tels que [pet=46] ou [pet=39].\n\nPendant que leurs familiers attaques, les Chasseurs font pleuvoir leurs projectiles sur leurs malheureuses cibles. Ils préfèrent s’évader du corps-à-corps et ralentir leurs ennemis pour s\'éloigner et lancer leurs salves mortelles. Ils sont aussi capable de poser des pièges pour infliger des dégâts, ralentir ou rendre impossible toutes actions de leurs ennemis.\n\nLes Chasseurs portent des armures intermédiaires (cuir/maille) et utilisent le mana pour faire des dégâts.\n[ul]\n[li] Il peut voyager très vite en utilisant [spell=13161] et le partager avec [spell=13159].[/li]\n[li] Ils ont un certain nombre de compétence accès sur la survie qu\'ils peuvent utiliser pour échapper ou éviter un danger potentiel, comme [spell=5384] et [spell=781].[/li]\n[li] Les Chasseurs spécialisés dans la [icon name=ability_hunter_swiftstrike][url=spells=7.3.51]Survie[/url][/icon] peuvent avoir [spell=53292], ce qui leur permet de fournir aux membres du raid le [spell=57669].[/li]\n[/ul]',NULL),(13,5,2,NULL,0,2,'[b][color=c5]Les Prêtres[/color][/b] sont généralement considérés comme l\'une des classes de soins les plus répandus dans World of Warcraft, car ils ont deux arbres de talents qui peuvent être utilisés pour guérir très efficacement. Les caractéristiques principales sont la puissance des sorts, l\'intelligence et l\'Esprit (s\'il s\'est spécialisé dans les soins).\n\nL\'arbre [icon name=spell_holy_holybolt][url=spells=7.5.56]Sacré[/url][/icon] comprend des talents qui renforcent fortement la guérison faite à leurs alliés, y compris des sorts qui peuvent être utilisés pour guérir plusieurs joueurs à la fois, comme [spell=48089]. \nL\'arbre de talent [icon name=spell_holy_wordfortitude][url=spells=7.5.613]Discipline[/url][/icon] se concentre principalement sur l\'absorption et l\'atténuation des dommages grâce à l\'utilisation de [spell=48066] et réduit les dégâts subis avec [spell=63944].\n\nLes Prêtres disposent d\'une grande palette d\'outils pour soigner, mais ils peuvent également sacrifier leurs soins pour infliger des dégâts grâce à la magie de l\'[icon name=spell_shadow_shadowwordpain][url=spells=7.5.78]Ombre[/url][/icon]. Ils sont alors capables d\'infliger des dégâts importants avec leurs capacités uniques et une fois qu\'ils se mettent en [spell=15473], leurs dégâts d\'ombre augmentent de manière significative tout en perdant la capacité de lancer des sorts du sacré.\n\nIl porte une armure en tissus, soigne les dégâts grâce à la magie du sacré mais inflige des dégâts grâce à la magie de l\'Ombre. Il utilise le mana comme ressource.\n[ul]\n[li] Fournissant les buffs les plus appréciés dans le jeu - [spell=48161], qui donne un buff d\'endurance indispensable à tout raid. Ils peuvent utiliser [spell=48073] et [spell=48169].[/li]\n[li] Les prêtres d\'ombre sont très sollicités dans n\'importe quel raid , fournissant le buff [spell=57669] pour stimuler la régénération de mana et peut même guérir leur propre groupe avec [spell=15286].[/li]\n[/ul]',NULL),(13,8,2,NULL,1,2,'[b][color=c8]Les Mages[/color][/b] sont les utilisateurs emblématiques de la magie en Azeroth, qui apprennent leur art au cours de leurs recherches et études approfondies. Ils maîtrisent la magie du feu, du givre et des arcanes pour détruire ou neutraliser leurs ennemis. Leurs caractéristiques principales sont la puissance des sorts et l’intelligence.\n\nIls portent des armures légères, mais compensent cette faiblesse par une puissante gamme de sorts offensifs et défensifs. Le mage fait donc des gros dégâts à distance, envoyant des boules élémentaires sur un ennemi isolé mais faisant pleuvoir la destruction sur une armée. En cas d\'attaque, il peut échapper aux combats rapprochés avec [spell=1953] et devient un [spell=45438] quand cela devient trop critique.\n\nLes Mages peuvent également augmenter les pouvoirs de leurs alliés : [spell=23028], les inviter à leurs [spell=43987] et même les faire voyager à travers des [url=spells=7.8.237&filter=na=portail]portails[/url]. Classe indispensable pour voyager en toute tranquillité. Ils utilisent le mana comme ressource. Les Mages :\n[ul]\n[li]Transforment leurs ennemis en créatures inoffensives ou les geler sur place grâce à [spell=122].[/li]\n[li]Utilisent [item=50045] pour avoir un élémentaire d\'eau en familier.[/li]\n[/ul]',NULL),(13,6,2,NULL,0,2,'[b][color=c6]Les Chevaliers de la mort[/color][/b] sont d\'anciens agent du Fléau, désormais alliés avec la Horde ou l\'Alliance. Cette classe de héros débute le jeu à haut niveau (55). Ses caractéristiques principales sont la force, sans oublier l\'endurance pour les tanks.\n\nTous leurs arbres de talent peuvent être utilisés pour faire des dégâts ou tanker.\n\nLes Chevaliers de la mort qui ont une affinité avec le [icon name=spell_deathknight_bloodboil][url=spells=7.6.770]Sang[/url][/icon] ont une grande capacité d’auto-guérison et peuvent fournir à un allié : [spell=49016] qui l’enrage à la vue du sang du champ de bataille.\nL’arbre de talent [icon name=spell_frost_freezingbreath][url=spells=7.6.771]Givre[/url][/icon] permet une augmentation significative de l’armure et spécialise le Chevalier de la mort dans les dégâts de zone avec [spell=49184]\nLes maîtres des maladies et des invocations sont les chevaliers de la mort [icon name=spell_deathknight_armyofthedead][url=spells=7.6.772]Impie[/url][/icon]. Ils peuvent utiliser leurs talents [spell=52143] et [spell=49206] pour être aidé lors des combats. Ils ont aussi une plus grande résistance à la magie grâce à la [spell=51052].\n\nLe chevalier de la mort utilise des runes comme ressource principale, dont chacun des trois types est utilisé pour différentes techniques.\n[ul]\n[li] Ils se battent avec les présences (semblable aux positions d\'un Guerrier) qui fournit des bonus spéciaux à leurs rôles.[/li]\n[li] Il dispose de plus de capacités à distance que la plupart de classes de corps à corps et privilégie les maladies et les dégâts infligés par ses familiers morts-vivants.[/li]\n[li] La classe de chevalier de la mort a sa propre capacité d\'enchantement d\'arme spéciale appelée [spell=53428], ce qui remplace le besoin d\'enchantements d\'armes classiques.[/li]\n[li] Ont accès à une zone spéciale inscrite inaccessible par toutes les autres classe : Acherus, le fort d’ébène, situé dans [zone=4298]. Où ils gagneront leurs points de talent en tant que récompenses de quêtes dans les premières heures de jeux.[/li]\n[/ul]\n\n[b]Montures de classe :[/b]\n[ul]\n[li] [spell=48778] - Niveau 55 - Bonus de Vitesse de 100%. [/li]\n[li] [spell=54729] - Niveau 60 - Bonus de vitesse : s’adapte à la compétence de monte. [/li]\n[/ul]',NULL),(13,7,2,NULL,0,2,'[b][color=c7]Les Chamans[/color][/b], maîtres des éléments et de la nature, apportent un grand nombre de buffs à tout un groupe sous forme de totem. Un Chaman peut appeler un totem de chaque élément : terre, feu, eau et air. Ces totems apparaissent à leurs pieds et sont actifs pour toutes les personnes du raid se trouvant dans la zone d’effet du totem. Un bon Chaman sait quels totems sont à lancer et dans quelles circonstances les utiliser, pour maximiser les dégâts du groupe et la survie.\n\nIls sont principalement des lanceurs de sorts, bien qu’un Chaman [icon name=spell_nature_lightningshield][url=spells=7.7.373]Amélioration[/url][/icon] aime se rapprocher des ennemis pour faire de gros dégâts. Il apprend l’[spell=30798] et peut utiliser le sort [spell=51533] pour invoquer 2 Esprits de Loups qui combattent avec lui. Bien qu’il soit principalement de mêlée, le Chaman Amélioration peut bénéficier de la puissance des sorts et lancer instantanément [spell=403] ou des soins avec le talent [spell=51530]. \n\nLes Chamans [icon name=spell_nature_lightning][url=spells=7.7.375]Élémentaires[/url][/icon] se tiennent en retrait pour lancer leurs sorts de feu et de foudre et infliger de grandes quantités de dégâts. Ils peuvent repousser leurs ennemis avec [spell=51490] et aussi les enraciner avec [spell=51486]. Ils apportent le [icon name=spell_fire_totemofwrath][url=spell=57722]Totem de courrou[/url][/icon] et le [spell=51470], buffs très recherchés dans les raids.\n\nLes Chamans qui choisissent [icon name=spell_nature_magicimmunity][url=spells=7.7.374]Restauration[/url][/icon] ont un grand panel de sort de guérison se qui leurs permets de se spécialiser dans le soin mono-cible ou multi-cible. Ils sont reconnus pour leurs puissantes [spell=1064] et pour créer un [spell=16190] qui aide la restauration de mana aux membres de leurs groupes. Ils gagnent aussi un puissant [spell=974], peuvent employer [spell=51886] pour enlever les malédictions, et ont un sort de guérison instantané : [spell=61295] qui soigne aussi au fil du temps.\n\nLes Chamans invoquent la puissance des éléments pour améliorer les dégâts de leurs armes ou sorts. Ils portent des armures moyennes, boucliers et utilisent le mana comme ressources.\n[ul]\n[li] Il peut apprendre plus de 20 totems différents.[/li]\n[li] Peuvent lancer [spell=32182] (ou [spell=2825]) pour amplifier les dégâts et les soins de tout le raid. Un buff unique très recherché.[/li]\n[li] Un chaman peut se transformer en [spell=2645] à partir du niveau 16 et peut même le rendre instantané avec le talent [spell=16287]. Ce sort ne peut être utilisé qu\'en extérieur.[/li]\n[li] Il ne peut avoir qu\'un seul bouclier élémentaire d\'actif sur lui [spell=324] ou [spell=52127]. Le [spell=974], peut-être posé sur un autre joueur.[/li]\n[/ul]',NULL),(13,11,2,NULL,0,2,'[b][color=c11]Les Druides[/color][/b] sont la « classe à tout faire » de World of Warcraft, c\'est-à-dire, capable de remplir tous les rôles : soigner, faire des dégâts à distance, faire des dégâts de mêlée ou tanker, en utilisant le Changeforme. Le druide offre donc aux joueurs de nombreux styles de jeu. Ses caractéristiques principales dépendent du rôle choisi.\n\nSous sa forme normale, c’est un lanceur de sorts qui peut se battre à distance et se soigner. Mais il peut aussi prendre d’autres formes dont des formes animales :\n\nLorsqu’un druide se transforme en [spell=5487] (et à un niveau plus avancé, [spell=9634]), son mana se change alors en rage, capable de charger sa cible, de la [spell=8983] et de subir des coups de plusieurs adversaires simultanément. C’est une forme orientée vers le tanking qui fournit une armure et de la vie supplémentaire. Il peut esquiver les coups, utiliser [spell=22812] pour augmenter sa résistance.\nQuand il se transforme en [spell=768], son mana se change alors en énergie, pouvant [spell=5215] tout en se déplaçant, d’augmenter parfois ça vitesse de courses de 70% et de bondir derrière ces ennemis pour attaquer avec le talent [spell=49376]. C’est une forme orienté vers les dégâts de mêlée en faisant saigner leur cible avec [spell=49800] ou [spell=62078] lorsque le druide est entouré d’ennemis.\nAvec les talents de druide équilibre, la [spell=24858] est réputé pour faire beaucoup de dégâts à distance notamment avec les sorts [spell=5176] et [spell=48505] qui peuvent être augmenté avec des points de talent. Il émet aussi une aura, qui augmente les coups critiques des sorts, très appréciée en raid.\nSa forme d’[spell=33891] (talent restauration) est conçue pour soigner sur la durée notamment avec les sorts [spell=33763] et [spell=48438]. Il émet une aura, qui augmente les soins de 6%. Il a la particularité d’avoir une grande régénération de mana.\n\nD’autres formes animales secondaires complètent cette liste : sa [spell=783] qui permet au druide d’augmenter sa vitesse de déplacement, sa [spell=1066] qui lui permet de respirer sous l’eau tout en nageant plus vite et sa [spell=33943] (et avec la compétence [spell=34091], la [spell=40120]) lui permet de voler instantanément.\n\n[ul]\n[li] Dans l’arbre de talent Combat farouche, les druides ont une aura [spell=17007] très utile pour tout groupe de raid.[/li]\n[li] Le sort [spell=20484] est utilisable en combat, mais à une recharge de 10 min.[/li]\n[li] Il possède le sort [spell=29166] qui lui permet de régénérer le mana très vite même en combat, sur lui ou tout autre membre.[/li]\n[li] Les Druides ont leur propre capacité de téléportation qui leur permet de voyager vers [zone=493], ce qui est utile lorsqu’ils ont besoin de s’entraîner.[/li]',NULL),(13,9,2,NULL,0,2,'[b][color=c9]Les Démonistes[/color][/b], vêtue d’armure légère, sont les maîtres des arts démoniaques. Ils possèdent des capacités très puissantes qui, si elles sont utilisées correctement, en font un adversaire formidable. Utilisant leurs malédictions en combinaison avec des sorts de dégâts directs, il cause des ravages et la destruction. Ses caractéristiques principales sont la puissance des sorts et l’intelligence.\n\nLes Démonistes qui ont choisi de se spécialiser dans l’arbre de talent Affliction, excellent dans l’utilisation des malédictions, ils posent sur leurs ennemis [spell=47865] pour les affaiblir ou [spell=47864] pour leurs faire des dégâts. Ils ont la [spell=18271] ce qui augmente les dégâts des sorts d’ombre de 25%.\nLe démonologue appel des démons pour l’aider dans ces combats, il emploie principalement l’[spell=30146]. Il peut aussi se [spell=59672] en démon pour augmenter ses dégâts durant une courte période.\nLe Démonistes destruction utilise des sorts de feu tels que [spell=5740] ou [spell=17962] pour infliger d’importants dégâts directs.\n\nLes Démonistes, tout en étant d’excellent dans les dégâts à distance, soutiennent beaucoup leurs alliés en appelant d’autre joueur avec [spell=698] ou en utilisant des magies rituelles pour conjurer des pierres imbues du pouvoir de guérir : [icon name=inv_stone_04][url=item=5509]Pierre de soin[/url][/icon].\n\n[ul]\n[li] Le démoniste est doté du sort [spell=1454] qui lui permet de sacrifier des points de vie pour régénérer son mana.[/li]\n[li] Le [spell=48020] lui permet une grande mobilité en annulant tous les effets de déplacement, et en s\'éloignant du corps-à-corps.[/li]\n[li] En utilisant le sort [spell=20022], le démoniste permet à la personne sur qui elle a été appliqué de ressusciter.[/li]\n[/ul]\n\n[b]Montures de classe :[/b]\n[ul]\n[li] [spell=5784], leurs yeux ne brûlent plus que d\'une haine inextinguible pour les démonistes qui les ont corrompus - Niveau 20 - Bonus de Vitesse de 60%. [/li]\n[li] [spell=23161] sont des destriers recréés qui ont été corrompus par les énergies infernales, transpirant et soufflant le feu - Niveau 60 - Bonus de vitesse : 100%. [/li]\n[/ul]',NULL),(8,81,2,NULL,0,2,'[b]Les Pitons du Tonnerre[/b] est la faction de la capitale des Taurens : [zone=1638], située dans la partie nord de la région de [zone=215]. L\'ensemble de la ville est construit sur des falaises à plusieurs centaines de pieds au-dessus du paysage environnant, elle est accessible par des ascenseurs sur les côtés sud-ouest et nord-est.\n\n[h3]Histoire[/h3]\n\nLa grande ville de Pitons du Tonnerre se trouve au sommet d\'une série de mesas qui donnent sur les prairies verdoyantes de Mulgore. Les Taurens, autrefois nomade, ont récemment construit la ville pour dresser un centre de caravanes commerciales avec des artisans itinérants et des artisans de toutes sortes. Elle a été établi par le puissant chef [npc=3057] après que les Taurens, avec l\'aide des Orcs, ont chassé les centaures qui habitaient à l\'origine Mulgore. De longs ponts de corde et de bois font la liaison entre les mesas qui sont surmontées de tentes, de longues maisons, de totems peints aux couleurs vives et de huttes spirituelles. Le chef de Tauren surveille la ville animée, en veillant à ce que les tribus unies de Tauren vivent en paix et en sécurité.\n\n[h3]Réputation[/h3]\n\n[npc=14728] offre une quête répétitive où il faut fournir des étoffes. Une fois exalté aux Pitons du Tonnerre, en récompense, les joueurs peuvent acheter des [url=?items=15.5&filter=na=Kodo;cr=93:92;crs=2:1;crv=0:0]kodos[/url].',NULL),(8,1038,2,NULL,0,2,'[b]Ogri\'la[/b] est un groupe d\'Ogres localisé dans [zone=3522], où leur proximité avec [item=32572] leur a permis d\'évoluer au-delà de leur nature brutale. Ils sont particulièrement impliqué dans une guerre contre le Dragon noir et la Légion ardente, qui cherchent les cristaux Apogides pour leurs propres fins.\n\n[h3]Localisation[/h3]\nOgri\'la est situé près du bord ouest des Tranchantes, entre le Camp de Forge: Terreur et le Camp de Forge: Courroux, juste à l\'ouest de Sylvanaar. Ogri\'la est seulement accessible en monture volante ou en forme de vol. Une autre alternative est d\'avoir une réputation d\'honoré ou plus élevé avec [faction=1031]. Mais un joueur doit avoir une monture volante pour atteindre le camp Garde Ciel près de Skettis.[pad]\n\n[h3]Reputation[/h3]\nLa reputation avec Ogri\'la ne peut être acquise que par quêtes, et il n\'y a que des quêtes répétables dont les [url=?quests&filter=da=ja;cr=1;crs=1038;crv=0]quêtes journalières[/url]. Il ya un plafond sur la quantité de réputation que l\'on peut obtenir chaque jour pour un joueur avec Ogri\'la, ce qui en fait une réputation \"difficile à farmer\".\n\n[b]Eclats Apogides[/b]\n[item=32569] peuvent être collectées de diverses manières. Ils peuvent être pillés sur le cadavres de monstres, recueillis à partir de l\'environnement, ou ils peuvent être en récompenses de quêtes terminées.[pad]\n[b]Cristaux Apogies[/b]\n[item=32572] se ramassent sur les élites de type Demons ou Dragons dans les Tranchantes. Pour appeler ces mobs, 35 Eclats Apogides sont nécessaires, et il est recommandé que vous ayez un groupe de 5 personnes pour les vaincre.\n\n[b]Quêtes[/b]\nIl y a un certain [url=?quests&filter=cr=1;crs=1038;crv=0]nombre de quêtes[/url] qu\'un joueur peut faire pour gagner de la réputation avec Ogri\'la, ainsi que plusieurs [url=?quests&filter=da=ja;cr=1;crs=1038;crv=0]quêtes quotidiennes[/url]. Beaucoup de quêtes quotidiennes seront également accordée à la réputation de la Garde Ciel Sha\'tari lorsqu\'elles seront complétées. \n\nPour accéder aux principales quêtes d\'Ogri\'la, un joueur doit d\'abord compléter les 5 quêtes de groupe de [npc=22941].\n\n[h3]Éléments épuisés[/h3]\nUn certain nombre d\'éléments apogides tombent parfois de mobs une fois mort. Lorsque vous avez amassé 50 éclats apogides, [url=?search=Apexis+Crystal+Infusion]les objets suivants peuvent être améliorés[/url], obtenant des statistiques supplémentaires et des emplacements de gemmes. Une fois ces objets améliorés, ils deviendront liés si équipés, et peuvent donc être vendus ou échangés avec d\'autres joueurs. Une chose à noter cependant, bien que les éléments épuisés peuvent également avoir des statistiques ou des effets, ils ne peuvent pas être équipés.',NULL),(8,911,2,NULL,0,2,'[b]Lune d\'Argent[/b] est la capitale des elfes de sang, située dans la partie nord-est de [zone=3430] dans le royaume de Quel\'Thalas. La capitale,des elfes de sang, est à couper le souffle. Elle peut rivaliser avec la capitale naine de [zone=1537], capitale la plus ancienne du monde toujours debout. Récemment reconstruite, la ville abrite la plus grande population d\'elfes de sang en Azeroth. \n\nAujourd\'hui, Lune d\'Argent n\'est que la moitié orientale de la ville d\'origine. La moitié occidentale a été presque entièrement détruite par le fléau pendant la troisième guerre. La place de lÉpervier, est la seule partie occidental de Lune d\'Argent restant sous le contrôle des elfes de sang. La Malebrèche, chemin parcouru par Arthas Menethil et son armée de morts-vivants parties en quête de ressusciter Kel\'Thuzad, traverse tout le Bois des Chants éternels. Il sépare la Lune d\'Argent reconstruite et ces ruines de la moitié occidentale. Fait intéressant, les ruines de Lune d\'Argent ne logent pas de morts-vivants, au lieu de cela, elles contiennent des [url=?npcs&filter=cr=37;crs=6;crv=1502;na=Déshérité;maxle=8]déshérités[/url] et des [npc=15638]. Dans l\'état actuel des choses, Lune d\'Argent est encore la plus grandes des villes Hordeuses.\n\n[h3]Histoire[/h3]\n\nLa ville de Lune d\'Argent a été fondée par les hauts élus après leur arrivée à Lordaeron, il y a des milliers d\'années. La ville a été construite en pierre blanche autour de plantes vivantes dans le style de l\'ancien Empire Kaldorei. La ville contenait les célèbres académies de Lune d\'Argent, centre d\'apprentissage de la magie arcane, et la Flèche de Solfurie, majestueux palais abritant la famille royale des hauts-elfes. Également basé dans la ville, la convocation de Lune d\'Argent, également connu sous le nom de « Le Concile de Lune d\'Argent », était l\'organe dirigeant des hauts-elfes. À travers une étendue d\'océan vers le nord, il y a l\'île qui contient le plateau du puits du Soleil.\n\nBien que Lune d\'Argent ait resorti relativement indemne de la deuxième guerre, dans la troisième guerre, le Chevalier de la mort Arthas a mené le Fléau dans la ville, l\'attaquant au cours de sa quête pour atteindre le puit du Soleil. Le roi High Elven a été tué et la majorité de la population a été exterminée. Les forces de fléau ont tenu la ville pendant un certain temps mais l\'ont abandonné après l\'épuisement de ses ressources. \n\nBien que la ville ait été attaquée par le Fléau, elle n\'est pas aussi détruite qu\'on pourrait le penser. Beaucoup de ses plantes sont mortes, quelques cadavres sont étendu sur le pavé, la ville était à l\'abri du feu et de la destruction. Lune d\'Argent ressemble maintenant à une ville fantôme, intacte, mais étrangement abandonnée. Néanmoins, les chasseurs de trésors fréquentent fréquemment les ruines de Lune d\'Argent pour essayer de trouver certains des artefacts précieux que les elfes ont laissés derrière avant de déserter la ville, mais les fantômes des anciens habitants de Lune d\'Argent les en empêchent.\n\n[h3]Réputation[/h3]\n\n[npc=20612] offre une quête répétitive où il faut fournir des étoffes. Une fois exalté à Lune d\'Argent, en récompense, les joueurs peuvent acheter des [url=?items=15.5&filter=cr=151;crs=6;crv=35513;na=Faucon-pérégrin]Faucon-pérégrins[/url].\n\nLes zones environnantes du Bois des Chants éternels et des terres fantômes contiennent la plupart des quêtes pour gagner de la réputation avec Lune d\'Argent.',NULL),(8,577,2,NULL,0,2,'[minibox]\n[h2]Cartel Gentepression[/h2]\n[faction=21]\n[b]Long-guet[/b]\n[faction=369]\n[faction=470]\n[/Minibox]\n\n[b]Long-guet[/b], faction de la ville du même nom, est un poste commercial dirigé par les gobelins du Cartel Gentepression. Il se trouve au carrefour des principales routes commerciales du [zone=618].\n\n[h3]Histoire[/h3]\n\nCette ville est le dernier point de la civilisation avant d\'atteindre le Mont Hyjal. Il est géré par les gobelins comme un poste commercial. La ville est officiellement neutre pour toutes les races et factions. Seuls les pèlerins peuvent monter jusquà lArbre-Monde, point culminant du Mont Hyjal. Long-guet est donc la destination la plus haute que les marchands et les aventuriers peuvent atteindre sans l\'autorisation des Elfes de nuit. Elle offrirait une vue dominante sur Kalimdor, si les nuages qui enveloppent continuellement les flancs de la montagne, disparaissaient.\n\nLong-guet est le seul avant-poste de gobelin majeur dans le nord de Kalimdor. Tout d\'abord, il sert de base aux opérations pour les mineurs de thorium et d\'arcanites puisque le Berceau-de-lHiver possède quelques veines inexploitées de ces matériaux. Deuxièmement, il sert de centre d\'échanges entre l\'Alliance et la Horde. Alors que Long-guet est à peine plus sûr que Reflet-de-Lune, généralement, l\'Alliance et la Horde se traitent assez bien là-bas. En outre, Long-guet est un point d\'arrêt et de réapprovisionnement fréquent pour les fidèles qui font le pèlerinage du Berceau-de-lHiver au Mont Hyjal.\n\n[h3]Réputation[/h3]\n\nLa réputation de Long-guet et du Cartel Gentepressin provient surtout des quêtes du Berceau-de-lHiver. Avec une réputation au minimum amicale, les gardiens vous aident en cas dattaque initiée contre vous.',NULL),(8,21,2,NULL,0,2,'[minibox]\n[h2]Cartel Gentepression[/h2]\n[b]Baie-du-Butin[/b]\n[faction=577]\n[faction=369]\n[faction=470]\n[/minibox]\n\n\n[b]Baie-du-Butin[/b] est une grande ville pirate nichée dans les falaises entourant un magnifique lagon bleu, à lextrémité de [zone=33]. Pour entrer dans la ville, il faut passer au travers les mâchoires blanchis d\'un requin géant.\n\nParcouru par les Écumeurs des Flots noirs qui sont étroitement associés eu Cartel Gentepression, le port offre des opportunités à n\'importe quel voyageur passant par là, indépendamment de leur faction. Combiné à la célèbre « taverne du Loup de mer », le [event=15], de nombreux maîtres de profession et des vendeurs, qui vendent de tout (des animaux de compagnie aux anneaux de diamant), c\'est l\'un des endroits les plus populaires en Azeroth.\n\n[npc=2496], chef de la ville, embauche toute l\'aide qu\'il peut obtenir contre [faction=87] et autres menaces de la ville. Il réside avec le chef des Écumeurs des Flots noirs, [npc=2487], au sommet de l\'auberge de Baie-du-Butin.\n\nEn raison de la liaison par bateau de Baie-du-Butin à Cabestan, les joueurs de tout niveau (surtout de la Horde, si le niveau est faible) peut-être croisés dans le port, bien que les visiteurs les plus fréquents seront dans les niveaux 35-45, car les quêtes disponibles auprès des gens du pays se situent dans cette tranche de niveau.\n\nL\'eau est parsemée de débris flottants et de bancs de poissons. Plusieurs types de poissons se pèchent dans les eaux de la Baie, tels que le [item=6359], le [item=6358], et l\'[item=13422]. La pêche, dans les débris flottants, vous donnera également plus de chance de pêcher des coffres et d\'autres articles, faisant de Baie-du-Butin un endroit idéal pour la pêche.\n\n[h3]Réputation[/h3]\nLa plupart des quêtes pour augmenter la réputation avec Baie-du-Butin sont situés au Cap de Strangleronce. Avec une réputation au minimum amicale, les gardes vous aiderons en cas dattaque contre vous.\n\nSi vous êtes haï avec Baie-du-butin vous pouvez faire la quête répétable [quest=9259] pour revenir à Neutre.',NULL),(8,470,2,NULL,0,2,'[minibox]\n[h2]Cartel Gentepression[/h2]\n[faction=21]\n[faction=577]\n[faction=369]\n[b]Cabestan[/b]\n[/Minibox]\n\n[b]Cabestan[/b], faction de la ville du même nom, situé sur la côte est de Kalimdor dans [zone=17]. Elle est dirigée par des gobelins. Ses rues se répandent dans toutes les directions, et l\'architecture ne montre aucune cohérence ni vision commune. C\'est une ville de divertissement et de commerce, où tout ce que vous voudriez acheter est en vente mais aussi beaucoup de chose que personne ne veut jamais. \n\nCabestan est actuellement géré par un groupe d\'entreprises connu sous le nom du Cartel Gentepression, un groupe fragmenté de la KapitalRisk, qui a d\'abord construit la ville portuaire pour la négociation avec [zone=1637]. C\'est d\'abord une faction neutre où Horde et Alliance se côtoient. Un bateau relie commodément Cabestan à Baie-du-butin.\n\n[h3]Histoire[/h3]\n\nConstruit à part égales entre l\'industrie et de la décadence, la ville portuaire gobeline de Cabestan s\'étend sur près d\'un kilomètre de littoral des Tarides de l\'est, entre [zone=14] et [zone=15]. Cabestan est la fierté des gobelins, une ville commerciale où vous pouvez trouver presque tout ce que votre cur désire, et si quelque chose n\'est pas en stock, vous pouvez parier que les gobelins peuvent le commander. Cabestan est desservie régulièrement par les bateaux qui font la traversé en passant devant la forteresse de Theramore, vers le sud.\n\nCabestan est une ville où les habitants, qui étaient autrefois des truands, règnent maintenant. Ses rues errent sans rime ni raison à travers des quartiers dédiés à une seule activité : le commerce. Des entrepôts délabrés se situent à côté de maisons en pierre majestueuses. Les belles boutiques sont voisines avec des cabanes grossières. Des objets de toutes les formes, et certains au-delà de l\'imagination, sont exposés sur les marchés et les boutiques exclusives.\n\nLes Gobelins accueillent toutes personnes ayant de l\'or, des éléments de valeur et une volonté de les échanger contre leurs marchandises et leurs services. Les marchands traversent la ville tous les jours, vendent tout, de la soie aux esclaves. Même la nuit, les magasins qui bordent les rues et les allées restent ouverts aux entreprises. Ceux qui ont de l\'argent peuvent écouter des musiciens qualifiés, tout en buvant des bières fines et en mangeant des aliments préparés par des grands chefs. Pour ceux qui ont des goûts plus terriens, on retrouve le long des quais des marchants d\'armes, la banque et des casinos.\n\nCabestan est le plus grand port de Kalimdor, beaucoup de navires transportant de la cargaison sortent pour d\'autres sites autour de Kalimdor. En plus des navires commerciaux légitimes, les bâtiments pirates reçoivent une amnistie dans le port de Cabestan tant qu\'ils peuvent payer des droits d\'accostage rigides. Cette situation rend les capitaines marchands furieux, mais ils ne peuvent boycotter Cabestan, sinon c\'est la faillite pour leurs commerces. En outre, les avocats et les mercenaires qui rôdent sur le front de mer sont impatients de faire face à tous ceux qui cherchent à causer des problèmes.\n\n[h3]Réputation[/h3]\n\nLa plupart des quêtes pour élever la réputation avec Cabestan et le Cartel Gentepression sont situées dans les Tarides. Avoir une réputation au minimum amicale, les gardiens aident en cas d\'attaque contre vous.\n\nSi vous êtes détesté auprès de Cabestan, vous pouvez faire la quête répétable [quest=9267] pour revenir à une réputation Neutre.',NULL),(8,369,2,NULL,0,2,'[minibox]\n[h2]Cartel Gentepression[/h2]\n[faction=21]\n[faction=577]\n[b]Gadgetzan[/b]\n[faction=470]\n[/minibox]\n\n[b]Gadgetzan[/b] est la faction de la ville du même nom, qui abrite les plus grands ingénieurs, alchimistes et marchands gobelins. Seul endroit de civilisation au nord du désert de [zone=440], elle est perçue comme une oasis. Gadgetzan est le siège du Cartel Gentepression, le plus grand cartel gobelin. Les gobelins croient au profit plus quà la loyauté, donc Gadgetzan est considéré comme territoire Neutre dans le conflit Horde / Alliance.\n\n[h3]Histoire[/h3]\n\nBien que la neutralité des gobelins soit presque universellement reconnue, il y a encore ceux qui cherchent à semer le chaos et lanarchie. Pour Gadgetzan, cela vient sous la forme des bandits Bat-le-désert, une bande de mécréants qui occupe le champ des Puisatiers et les ruines d\'Ombre-du-Zénith au Nord-est de Tanaris. Peu de Gobelins se soucient des ruines antiques (à moins quils y aient un trésor), les bandits peuvent avoir les vieux blocs de pierre. \nCependant, le champ des Puisatiers est vital pour la survie des gobelins, leur fournissant lor liquide du désert. Les tours d\'eau dans le champ ont été construites sous la chaleur ardente du soleil, par le travail de leurs esclaves. Les gobelins ne vont pas abandonner leurs tours durement gagnées, aussi facilement. Mais, ils doivent rester en ville pour arrêter le conflit, en apparence interminable, parmi les différents visiteurs et donc empêcher de perturber les affaires. Par conséquent, ils embauchent de braves mercenaires venant de tous les coins du monde pour les aider.\n\n[h3]Réputation[/h3]\n\nEn tuant les [url=?npcs=7&filter=na=mers+du+Sud]Flibustiers des mers du Sud[/url] et les [url=?npcs=7&filter=na=bat-le-désert]Bandits Bat-le-désert[/url], la réputation avec le cartel Gentepression augmentera. Ayant une réputation au minimum amicale, les gardes vous aideront en cas d\'attaque contre vous. Avoir une réputation exaltée signifie que les gardes ne vous attaqueront jamais même si vous lancez des attaques sur la faction opposée. \n\nLa plupart des quêtes associées à la faction Gadgetzan sont situées à Tanaris. \n\nSi vous êtes détestés avec Gadgetzan, vous pouvez faire la quête répétable [quest=9268] pour obtenir la Neutralité.',NULL),(8,47,2,NULL,0,2,'[b]Forgefer[/b] est la faction associée à la capitale des nains, [zone=1537]. [npc=2784] règle son royaume de Khaz Modan de sa salle du trône dans la ville, et [npc=7937], chef des gnomes, a temporairement dû s\'établir dans Brikabrok après la récente chute de la ville gnome [zone=133].\n\n[h3]Histoire[/h3]\n\nForgefer est l\'ancienne demeure des nains, une merveille façonnée dans la pierre. Forgefer a été construite au cur même des montagnes, une ville souterraine qui abrite des explorateurs, des mineurs et des guerriers. Les portes massives de roche protègent la ville en temps de guerre, et la lave de la montagne est redirigée et distribuée à des fins de chaleur, d\'énergie et de forage. \nAvant que le clan de Sombrefer ne soit banni de la ville, menant à la Guerre des Trois Marteaux, Forgefer était le centre commercial et social de tous les clans nains. Il appartient maintenant au Clan Barbe-de-bronze. \nBeaucoup de bastions nains ont chuté pendant la Guerre de Lordaeron, entre la Horde et l\'Alliance, mais la puissante ville de Forgefer, nichée dans les sommets hivernaux de [zone=1] et protégée par ses grandes portes, n\'a jamais été violée par la Horde envahissante.\n\nRelativement récemment, Forgefer est également devenu le foyer des Exilés de Gnomeregan. Après la troisième guerre, la ville gnome fut envahie par Troggs. Depuis lors, un certain nombre de gnomes se sont installés à Forgefer, transformant une zone de cette ville à leur goût, une région connue sous le nom de Brikabrok.\n\nForgefer est l\'une des villes les plus peuplées du monde, venant après la ville humaine de [zone=1519], et abritant 20 000 personnes.\n\nAlors que l\'Alliance a été affaiblie par les événements récents, les nains de Forgefer, dirigés par le roi Magni Barbe-de-bronze, forment un nouveau futur dans le monde. \n\n[h3]Réputation[/h3]\n\n[npc=14723] offre une quête répétitive où il faut fournir des étoffes. Une fois exalté à Forgefer, en récompense, les joueurs peuvent acheter des [url=?items=15.5&filter=cr=93:92:151:151;crs=2:1:6:6;crv=0:0:33977:33976;na=bélier] béliers [/url].\n\nLes zones environnantes [zone=1], [zone=38] et [zone=11] contiennent la plupart des quêtes pour gagner de la réputation auprès de Forgefer.',NULL),(8,54,2,NULL,0,2,'[b]Les Exilés de Gnomeregan[/b] est la faction des gnomes qui ont fui leur domicile, [zone=133] à [zone=1]. Elle a été détruite par [url=?npcs=7&filter=na=Trogg] les Troggs[/url] après une invasion toxique. Maintenant, membre de lalliance, la plupart sont situés à Brikabrok, une partie de la ville voisine [zone=1537], y compris le leader [npc=7937].\n\n[h3]Histoire[/h3]\n\nOn a spéculé que les gnomes ont été formés comme des robots par les titans, en raison de leur nature curieuse et de leurs compétences techniques. Ils vivaient autrefois dans la cité de Gnomeregan, sans doute la plus belle ville technologique du monde.\n\nLes gnomes étaient une race souterraine de bricoleurs, jusquà ce que les Troggs aient détruit Gnomeregan. Dans cette guerre, plus de 80% de la population gnome a été exterminé.\n\n[h3]Réputation[/h3]\n\n[npc=14724] offre une quêtes répétables où il faut fournir des étoffes. En étant exalté aux Exilés de Gnomeregan, les joueurs sont capables de conduire des [url=?items=15.5&filter=cr=93:92;crs=2:1;crv=0:0;na=mécanotrotteur]mécanotrotteurs[/url].\n[zone=1] contient la plupart des quêtes pour gagner la réputation avec les exiés de Gnomeregan.',NULL),(8,72,2,NULL,0,2,'[b]Hurlevent[/b] est la faction associée à [zone=1519], la capitale des Humains. Elle est située dans la partie nord-ouest de la [zone=12]. L\'enfant roi, [npc=1747], réside dans le Donjon de Hurlevent, entouré de ses gardes du corps et de ses conseillers, [npc=1748] (le régent) et [npc=1749]. La ville est nommée ainsi à cause des rafales soudaines et occasionnelles créées par la forme spéciale des montagnes autour de la ville glorieuse.\n\n[h3]Histoire[/h3]\n\nPendant la Première Guerre, le Royaume d\'Azeroth, y compris sa capitale, le Donjon de Hurlevent, a été complètement détruit par la Horde. Ses survivants ont fui vers Lordaeron. Après que les orcs ont été vaincus, au Portail des Ténèbres, à la fin de la Deuxième Guerre, il a été décidé que la ville serait reconstruite, dépassant sa grandeur dantan. Des tailleurs de pierres et des architectes ont pu été rassemblés par les nobles de Hurlevent. Sous la directio de cette équipe, la plus qualifiée et la plus ingénieuse, Hurlevent a été reconstruit dans une période de temps incroyablement courte. Maintenant, à la fin de la troisième guerre, dans le renommé Royaume de Hurlevent. Cest l\'un des derniers bastions du pouvoir humain laissé dans le monde.\n\nAvec la chute des Royaumes du Nord, Hurlevent est de loin la ville la plus peuplée du monde. Avec une population de deux cents mille personnes (principalement humaines), elle sert à bien des égards comme le centre culturel et commercial de l\'Alliance, même avec un accès à la mer. Les humains qui vivent dans la ville sont généralement insouciants et artistiques, favorisant les vêtements légers et colorés, la cuisine et l\'art. Elle abrite l\'Académie des sciences arcanes, la seule école de sorcellerie dans les royaumes de l\'Est, ainsi que le SI:7, une organisation de renseignement.\n\nCependant, les gens de Hurlevent ont du mal à accepter le rôle de Theramore en tant que foyer de la nouvelle Alliance. Ils sont convaincus que Hurlevent devrait être l\'héritière légitime du rôle de la ville de Lordaeron comme par le passé, mais aussi que Theramore est attristé face à l\'aggravation de la situation au sein de Les Royaumes de l\'Est.\n\n[h3]Réputation[/h3]\n\n[npc=14722] propose une quête répétable pour obtenir une réputation plus élevée avec Hurlevent. En contrepartie d\'une réputation exaltée, les joueurs non-humains peuvent monter sur des chevaux.\n\nLa plupart des quêtes associées à Hurlevent viennent des zones environnantes de la forêt d\'Elwynn, [zone=40] et [zone=44].',NULL),(8,930,2,NULL,0,2,'[b]Exodar[/b] est la faction associée à [zone=3557], la capitale enchantée des Draeneï construit avec la plus grande partie de leur vaisseau qui sest écrasé. Il est situé dans la partie ouest de l[zone=3524]. Le chef de la faction Exodar est [npc=17468], qui est situé près des maîtres de combat dans la Voûte des Lumières.\n\n[h3]Histoire[/h3]\n\nLes Draeneï rescapés du crash de leur vaisseau se sont récemment réveillés pour reconstruire lExodar, encore fumant de limpact. L\'Exodar était autrefois une structure de satellite naaru autour de la forteresse dimensionnelle du [url=?search=donjon+tempête]Donjon de la Tempête[/url]. L\'Exodar contient une grande quantité de merveilles technologiques (en raison de ses origines avec le Donjon), comme des «fils» magiquement enchantés qui transmettent de l\'énergie sainte dans tout le navire pour alimenter le chauffage et l\'éclairage, tout en augmentant les pouvoirs, déjà considérable, des Draeneï.\n\n[h3]Réputation[/h3]\n\nComme pour les autres grandes factions associées aux races principales, la réputation de l\'Exodar peut être acquise en faisant la quête répétable de [npc=20604] [small][/small], ou alors, en tuant la faction adverse dans [zone=2597] (les elfes de sang) et en faisant les quêtes appropriées. Avec la réputation, le joueur peut acheter des objets provenant de fournisseurs liés à Exodar pour 10% de moins et, une fois exalté, le joueur peut acheter [url=?Items=15.5&filter=na=elekk;cr=93:92;Crs=2:1;crv=0:0] diverses montures[/url].',NULL),(8,69,2,NULL,0,2,'[b]Darnassus[/b] est la faction de la ville de [zone=1657], la capitale des Elfes de la nuit. La haute prêtresse, [npc=7999], réside dans le Temples de la Lune, entourée d\'autres surs d\'Elune. Dans l\'Enclave Cénarien, l\'[npc=3516] conduit le [faction=609], souvent en opposition directe avec ses autres druides à [zone=493] et Tyrande elle-même.\n\n[h3]Histoire[/h3]\n\nAu lendemain de la troisième guerre, les Elfes de la nuit devaient s\'adapter à leur existence mortelle. Un tel ajustement était loin d\'être facile. Beaucoup d\'Elfes de la nuit ne pouvaient pas s\'adapter aux perspectives de vieillissement, de maladie et de fragilité. En cherchant à retrouver leur immortalité, un certain nombre de druides capricieux conspiraient pour planter un arbre spécial qui rétablirait un lien entre leurs esprits et le monde éternel.\n\nAvec [npc=15362] disparu, Fandral Forteramure, le chef de la conspiration qui souhaitaient planter le nouvel Arbre-Monde, est devenu le nouvel Archidruide. En un rien de temps, lui et ses camarades druides ont pris les devants et ont planté le grand arbre, [zone=141], au large des côtes orageuses du nord de Kalimdor. Avec leur soin, l\'arbre a poussé au-dessus des nuages. Parmi les branches crépusculaires de l\'arbre colossal, la merveilleuse ville de Darnassus a pris racine. Cependant, l\'arbre n\'a pas été béni par la nature et s\'avère être corrompu par la Légion Ardente. Maintenant, la faune et même les membres de Teldrassil sont contaminés par une obscurité croissante.\n\n[h3]Réputation[/h3]\n\n[npc=14725] offre une quête répétable [quest=7800] utilisé par les joueurs de l\'Alliance pour obtenir le droit de monter des [url=?items=15.5&filter=cr=93:92:151;crs=2:1:6;crv=0:0:13086;na=sabre;si=-1]Sabres-de-nuit[/url]. Les joueurs qui sont au minimum niveau 44, cherchant à gagner la faveur de Darnassus, devraient trouver et compléter les quêtes de [zone=357]. Les quêtes sont associées à Darnassus et pourraient accroître considérablement votre réputation.',NULL),(8,809,2,NULL,0,2,'Les [b]Shen\'dralar[/b] sont la faction des Elfes de nuit restant dans [zone=2557]. Ils sont un groupe qui pratique la magie arcane à son apogée sur les traces de leur ancienne reine Azshara, et de ses partisans, les Bien-nées. Ils vivent à Eldre\'Thalas (nom antérieur de Hache-tripes) depuis la fin de la guerre des Anciens. Ils sont peu nombreux, mais leur connaissance et leur pouvoir mystique sont géniaux.\n\nLeur chef, [npc=11486], était chargé de superviser la construction des pylônes pour contenir le grand démon [npc=11496] et absorber son pouvoir démoniaque. Après de longues et nombreuses années, le pouvoir des pylônes a commencé à diminuer, le prince a entrepris de tuer les elfes de nuit restants pour maintenir l\'énergie. Les esprits des défunts demandent vengeance, mais seuls des aventuriers aguerris peuvent le tuer. Faite-vite, il reste très peu d\'habitants en vie.\n\n[h3]Réputation[/h3]\n\nLa réputation peut être obtenue en rendant à plusieurs reprises les quêtes obtenus avec les trois Librams de Hache-Tripes : [item=18333], [item=18334] et [item=18332]. \nLa réputation peut être obtenue aussi via les livres de classe suivant :\n[ul] \n[li] [item=18357] - Guerrier [/li] \n[li] [item=18363] - Chaman [/li] \n[li] [item=18356] - Voleur [/li] \n[li] [item=18360] - Démoniste [/li] \n[li] [item=18362] - Prêtre [/li]\n[li] [item=18358] - Mage [/li]\n[li] [item=18364] - Druide [/li]\n[li] [item=18361] - Chasseur [/li]\n[li] [item=18359] - Paladin [/li]\n[li] [item=18401] - Guerrier et Paladin [/li] \n[/ul] \nLes livres de classe et les librams donnent 500 points de réputation chacun.',NULL),(8,349,2,NULL,0,2,'[b]Ravenholdt[/b] est une guilde de voleurs et d\'assassins qui ne reçoit que ceux d\'une extraordinaire prouesse. Ils sont opposés à la [faction=70]. La quête, [quest=8249], est disponible pour les classes non-voleurs, mais elle nécessite l\'aide d\'un voleur pour obtenir les objets pour la quête. Le manoir de Ravenholdt, le siège de la faction, est situé dans [zone=36], mais pour y arriver, vous devez venir du coin nord-est de [zone=267].\n\n[h3]Réputation[/h3]\n\nTous les [url=?Search=Syndicat#npcs]membres du Syndicat [/url] donnent 1-5 points de réputation en fonction de votre niveau actuel. De plus, il existe quelques quêtes qui augmentent votre réputation, mais la méthode principale pour élever votre réputation provient des quêtes répétées pour fournir les objets demandés.\n\nVous commencez à une réputation Neutre (0/3000) avec Ravenholdt, ce qui signifie que si vous tuez un NPC de Ravenholdt avant d\'augmenter votre réputation d\'au moins 5, vous deviendrez hostile et ne pourrez jamais augmenter votre réputation. \nPour augmenter votre réputation de Neutre à Amicale, la quête répétable [quest=6701] est disponible. Vous devrez fournir 11-12 [item=17124] et une fois que vous êtes amical, cette quête n\'est plus disponible. Vous pouvez également fournir cinq [item=16885].\nPour augmenter votre réputation au-delà de Amical, le seul choix est la quête répétable, [quest=8249]. \n\n[h3]Récompense[/h3]\n\nIl n\'y a aucune récompense de faction connue pour obtenir que se soit avec une réputation Amicale, un honoré, révéré ou exalté, sauf que les gardes vous parlent avec plus de respect. \n\nCependant, La réputation Exalté est nécessaire pour obtenir le Haut-Fait : [achievement=2336].',NULL),(8,87,2,NULL,0,2,'Les [b] Pirates de la Voile Sanglante [/b] semblent être l\'une de ces organisations, qui sont apparues en Azeroth pendant les événements menant à la troisième guerre et à la suite de la troisième guerre. Ils sont originaires du Rivage Cruel, où leur chef, l\'[npc=2546], organise les opérations. Ils ont maintenant l\'intention de paralyser et de piller la ville portuaire de [faction=21], contrôlée par le Cartel Gentepression et sous la protection des Ecumeurs des Flots noirs. Il est probable que les Pirates de le Voile Sanglante sont venus profiter de la perte actuelle de leur flotte, sur la côte de la [zone=45], dans laquelle deux de ses navires ont été détruits. Le navire restant a été obligé de trouver un abri dans une crique où son équipe lutte maintenant pour survivre aux escarmouches des Nagas.\n\nEn préparation de l\'attaque, les Pirates de la Voile Sanglante ont pris position dans des endroits clés près de la ville. À l\'heure actuelle, ils ont trois navires ancrés le long du littoral au sud de Baie-du-Butin, à l\'abri des canons défensifs de la ville. Des camps ont également été construits le long de la même côte en prévision de l\'attaque. En outre, une fête scoute a atterri juste à l\'ouest de l\'entrée de la ville, signalant toutes les activités, ainsi qu\'un camp construit le long de la route menant vers la ville, susceptible d\'empêcher tout renfort.\n\nLes Pirates de la Voile Sanglante cherchent à atteindre leurs objectifs sans avoir leurs forces engagées dans la bataille, à cette fin, chaque côté cherche maintenant l\'aide d\'aventuriers sympathiques à leur cause.\n\n[h3]Réputation [/h3]\n\nIl n\'y a qu\'une seule façon d\'augmenter votre réputation auprès des Pirates de la Voile Sanglante et c\'est de libérer votre colère contre tous les citoyens de Baie-du-Butin. Voici une liste de tous les citoyens de Baie-du-Butin et leur valeur de réputation. \n[ul]\n[li] [npc=4624] : 25 points de réputation gagné [/li]\n[li] [npc=15088] : 25 points de réputation gagné [/li]\n[li] [npc=2496] : 5 points de réputation gagné [/li]\n[li] [npc=2636] : 5 points de réputation gagné [/li]\n[li] [url=?Npcs&filter=cr=3;crs=21;crv=0] Plusieurs autres NPC [/url][/Li]\n[/Ul]\nLe montant gagné avec les Pirates de la Voile Sanglante est indiqué pour un niveau 60 non humain. Le montant perdu pour tuer un citoyen ne peut pas être démontré car il dépend de votre niveau actuel avec Baie-du-Butin et de l\'importance de la personne que vous tuez. En plus de cela, quand vous perdez de la réputation avec Baie-du-Butin, vous perdez la moitié dans les trois autres villes du Cartel Gentepression. Par exemple, si vous perdez 25 points avec Baie-du-Butin, vous perdrez 12,5 points avec [faction=470].\n\nLe moyen le plus rapide d\'augmenter votre réputation avec les Pirates de la Voile Sanglante est de tuer des habitants de Baie-du-Butin. Au début, cela peut sembler une tâche simple car les gardes n\'apparaissent pas aussi menaçants que les autres monstres auxquels un joueur est confronté dans le jeu. Cependant, les gardes sont très équipés pour neutraliser les joueurs de toute classe, afin d\'éviter que les gens ne s\'attaquent les uns les autres dans la ville. \n\nLe Cogneur de Baie-du-butin a l\'avantage avec plusieurs capacités. Lune dentre elle est lutilisation de filet pour vous bloquer sur place, vous empêchant de vous échapper. Une autre est le fait qu\'ils appellent dautres Cogneurs chaque fois que vous attaquiez un citoyen de la ville ou si vous êtes sous un statut hostile avec Baie-du-Butin, les joueurs peuvent bientôt se retrouver rapidement submergés par les Cogneurs.\nLa capacité la plus forte du Cogneur est quune fois qu\'il tire son arme, il est peu probable que vous vivez, si vous ne vous échappez pas assez vite. Chaque fois qu\'un Cogneur vous tire dessus, l\'attaque vous retient, tout comme une attaque de marteau d\'Ogre. La différence ici, est que le Cogneur peut tirer rapidement en succession, provoquant des lances de chaîne. Un joueur peut littéralement être jeté d\'un côté de la ville à l\'autre, ce qui vous empêche d\'attaquer. Plus souvent, vous vous retrouverez coincé dans un coin, incapable de bouger et incapable d\'attaquer avec tous les sortilèges interrompues par l\'attaque du Cogneur. Parce que les Cogneurs ne rangent pas leurs armes à feu une fois qu\'elles sont sorties, la meilleure façon d\'agir est de s\'enfuir.\n\nPar essais et erreurs, la plupart des gens ont découvert un endroit sûr pour tuer les Cogneurs de Baie-du-Butin. Si vous suivez le tunnel qui mène à la ville, le chemin de votre gauche qui mène à la maison du Forgeron est l\'endroit idéal pour tuer les gardes. Seuls deux gardes patrouillent sur ce chemin. Une fois qu\'ils sont partis, entrer dans la première construction sur le chemin pour provoquer un rassemblement. Un joueur devrait pouvoir tuer 2 à 4 Cogneurs avant que les deux Cogneurs de patrouille en appellent dautres. En moyenne, un joueur qui fait cela peut tuer environ 30 à 40 Cogneurs de Baie-du-Butin, gagnant environ 800 points de réputation auprès de la Voile Sanglante. Les Cogneurs ici ne semblent pas sortir leurs armes, mais si vous vous trouvez dans une mauvaise situation, vous pouvez sauter sur la balustrade, courir sur le chemin des eaux, pour vous échapper.\n\nPour augmenter votre réputation au-delà de honoré, seuls deux NPC vous le permettent : \n[ul]\n[li] [npc=9179] : 5 points de réputation toutes les 7 minutes jusquà révéré [/li]\n[li] [npc=26081]: 5 points de réputation toutes les 24 heures jusquà exalté [/li]\n[/Ul]\n\n[h3]Récompenses[/h3]\n\nDevenir amical avec Les Pirates de la Voile Sanglante, vous donnera accès aux éléments suivants :\n[ul]\n[li] [item=12185] - Invoque un [npc=11236] [/li]\n[li] [item=22742] [/li]\n[li] [item=22743] [/li]\n[li] [item=22745] [/li]\n[/Ul]\nVous aurez besoin d\'être honoré avec la Voile Sanglante pour [achievement=2336].',NULL),(8,70,2,NULL,0,2,'Le[b] Syndicat [/b] est une organisation criminelle humaine qui opère principalement dans les [zone=45] et les [zone=36], bien que quelques petits campements soient éparpillés dans les [zone=267]. Leur effectif compte environ 3 000 personnes.\n\nIls ont trois chefs : [npc=2423], descendant du premier Lord d\'Alterac, qui dirige les actions du Syndicat dans les montagnes Alterac, [npc=2597] dirige les actions du Syndicat dans les Hautes Terres d\'Arathi à partir de la principale demeure, le Donjon semi-abandonnée de Stromgarde, et Lady Beve Perenolde, fille d\'Aiden Perenolde.\n\n[h3]Histoire[/h3]\n\nPendant la seconde guerre, Lord Perenolde qui dirige le royaume d\'Alterac, a été découvert pour être en liaison avec les orcs de la Horde. Perenolde croyait qu\'une victoire de le Horde était inévitable et offrait ainsi une aide à la Horde en suscitant des rébellions, en attaquant les bases de l\'Alliance et en leur fournissant des armes. Lorsque cette trahison fut découverte, l\'Alliance marchait contre Alterac et la détruisit. Perenolde et tous les nobles qui ont accompagné ses projets ont été dépouillés de leurs titres et de leurs terres. Beaucoup d\'entre eux ont réussi à s\'échapper, mais ont commencé à comploter pour se venger. En utilisant leur fortune encore considérables, la noblesse a engagé une bande de voleurs et d\'assassins, formant une organisation connue sous le nom de Syndicat.\n\nAu début, le but du Syndicat était simplement de répandre le chaos et le désordre, frappant des bases cachées dans les montagnes d\'Alterac. Avec la fin de la troisième guerre et le chaos qui suivie, les dirigeants du Syndicat ont vu leur chance de reprendre Alterac et de retrouver leurs anciens pouvoirs. Ils ont maintenant pris le contrôle de plusieurs avant-postes dans la région environnante, y compris le donjon abandonnée et une partie de la ville de Stromgarde.\n\nIls sont haïe par l\'Alliance, qu\'ils considèrent comme leurs ennemis mortels, et la Horde, qu\'ils considèrent comme des brutes faits pour travailler en esclaves. En conséquence, le Syndicat est maintenant chassé par les deux factions, avec [npc=10181], en particulier, une prime est sur sa tête, tous les membres du Syndicat capturés seront exécutés sommairement. En outre, [npc=4949] a commandé un certain nombre de ses agents, y compris [npc=2229], [npc=2239], [npc=2238] et leur chef [npc=2316] pour lancer une enquête sur la nature du Syndicate et ses activités, ainsi que pour récupérer [item=3498], un collier maintenant porté par Elysa, la maîtresse de Lord Aliden, qui appartenait à un son cher ami, [npc=18887].\n\n[h3]Réputation[/h3]\n\nLe Syndicat, en tant que faction dans World of Warcraft, est très étrange par rapport à la plupart des factions. En effet, que le meurtre des membres de cette faction ne réduira pas votre réputation. Pour la plupart des joueurs, qui ne sont pas voleur, la seule façon d\'afficher le Syndicat dans leur menu de réputation est de compléter la quête [quest=8249]. Cependant, la quête requiert [item=16885] ... que seuls les voleurs peuvent obtenir en volant à la tir des PNJ au-dessus du niveau cinquante ce qui rend difficile d\'organiser une telle transaction.\n\nActuellement, il n\'y a qu\'une seule option connue pour augmenter la réputation d\'un joueur avec le Syndicat, en tuant des membres de la faction [faction=349]. Il n\'y a pas de récompenses connues pour avoir augmenté la réputation du Syndicat. Les PNJ affiliés à Ravenholdt ne donnent que 1 point de réputation, à l\'exception de [npc=13085], qui donne 5 (bien que la perte de réputation correspondante avec Ravenholdt soit aussi cinq fois plus grande ). Tous les joueurs commençent à une réputation détestée de 32000/36000, il faudrait tuer 10 000 PNJ de Ravenholdt pour atteindre le statut neutre avec la faction. Malheureusement, l\'état neutre est le plus élevé que vous puissiez atteindre avec le Syndicat, ce n\'est pas pour dissuader les joueurs, aucun des NPC Ravenholdt ne grimpe la réputation.\n\n[b]AVERTISSEMENT[/b]: Si vous décidez de tuer les PNJ de Ravenholdt, sachez qu\'il n\'y a actuellement aucun moyen de restaurer votre positionnement avec Ravenholdt, si vous passez en dessous de Neutre. La raison du problème est qu\'aucune des quêtes qui donnent des points de réputation de Ravenholdt ne sera disponible car aucun des membres de Ravenholdt ne vous parleront. Cela signifierait qu\'il s\'agit d\'un changement permanent et que vous ne pourrez plus jamais interagir avec l\'un des NPC fidèles à Ravenholdt. Notez également que les joueurs commencent à la réputation de 0/3000 avec Ravenholdt, et le fait de tuer même un de leurs PNJ à ce niveau de réputation vous empêchera pour toujours de rétablir votre réputation avec eux.',NULL),(8,59,2,NULL,0,2,'[b]La Confrérie du Thorium[/b] est un groupe d\'artisans d\'élite qui vend un certain nombre de recettes épiques, par contre, vous devez obtenir suffisamment de réputation avec eux. Tous les joueurs commencent à la réputation : Neutre.\n\n[h3]Histoire[/h3]\n\nLa [zone=51] abrite un groupe de nains exceptionnellement robustes qui se sont séparés du Clan Sombrefer. Sur les falaises surplombant la région appelée « Le Chaudron », dans le grand nord des Gorges des vents brulants, les nains de la Confrérie du Thorium ont établi une base d\'opérations, la Halte du Thorium. De là, ils surveillent de près les activités des nains de Sombrefer dans les Gorges des vents brûlants. Les aventuriers qui cherchent la Halte du Thorium trouveront que les nains de la Confrérie du Thorium qui donnent de grandes récompenses pour ceux qui les aident dans leur lutte sans fin contre leurs anciens frères.\n\nLa Confrérie du Thorium comprend de nombreux artisans exceptionnellement talentueux, et les forgerons de la Confrérie sont censés être parmi les meilleurs Azeroth. Ils possèdent les connaissances requises pour fabriquer les armes et les armures de [npc=11502], le Seigneur du Feu, mais n\'ont pas de main-d\'uvre pour obtenir les matériaux nécessaires à l\'artisanat. On raconte qu\'un membre de la Confrérie du Thorium a été habilité à échanger les recettes et les projets fabuleux des nains avec ceux qui peuvent prouver leur fidélité à la Confrérie. Bien sûr, pour prouver sa fidélité, l\'aventurier doit s\'aventurer au coeur de [zone=2717], le domaine de Ragnaros, le Seigneur du Feu lui-même, pour fournir aux nains les matières premières rares trouvées là-bas. Une tâche ardue, sans aucun doute, mais avoir accès aux secrets de la Confrérie du Thorium devrait s\'avérer être une récompense qui vaut bien l\'effort.\n\n[h3]Réputation[/h3]\n\n[b]De Neutre à Amical[/b]\n[ul]\n[li] Fournir : [item=18944], [item=3857] et [item=4234], [item=3575] ou [item=3356] au [npc=14624]. [/Li]\n[/ul]\n[b]De Amical à Honoré[/b]\n[ul]\n[li] Fournir : [item=18945] au [npc=14624]. [/Li] \n[/ul]\n[b]De Honoré à Exalté[/b]\n[ul]\n[li] Fournir : [item=11370] à [npc=12944]. [/Li]\n[li] Fournir : [item=17012] à Lokhtos Sombrescompte. [/Li]\n[li] Fournir : [item=17010] à Lokhtos Sombrescompte. [/Li]\n[li] Fournir : [item=17011] à Lokhtos Sombrescompte. [/Li]\n[li] Fournir : [item=11382] à Lokhtos Sombrescompte. [/Li] \n[/ul]',NULL),(8,68,2,NULL,0,2,'[b]Fossoyeuse[/b] est la faction pour la capitale du même nom, [zone=1497], régie par Sylvanas Coursevent. La cité est situé dans la [zone=85], au bord nord des Royaumes de l\'Est. La ville proprement dite est sous les ruines de la ville historique de Lordaeron. Pour y entrer, vous traverserez les défenses extérieures en ruines de Lordaeron et la salle du trône abandonnée, jusqu\'à ce que vous atteigniez l\'un des trois ascenseurs gardés par deux abominations.\n\n[h3]Histoire[/h3]\n\nFossoyeuse était à l\'origine un système d\'égouts, de cryptes et de catacombes sous la capitale de Lordaeron. Après que la ville a été détruite par le Fléau, Arthas a reconstruit et agrandit le dédale de souterrain. Initialement, il voulait que Fossoyeuse soit son siège de pouvoir, d\'où il gouvernerait les terres de pestes. Cependant, peu de temps après la fin de la troisième guerre, Arthas a été obligé de retourner à Norfendre et de sauver le Roi Liche. En son absence, [npc=10181] et ses non-morts rebelles ont capturé les ruines de la ville. Peu de temps après, elle a découvert la grande forteresse souterraine et a décidé de l\'établir comme base principale des opérations pour les Réprouvés.\n\n[h3]Réputation[/h3]\n\n[npc=14729] offre une quête répétitive où il faut fournir des étoffes. Une fois exalté à Fossoyeuse, en récompense, les joueurs peuvent acheter des [url=?items=15.5&filter=cr=93:92;crs=2:1;crv=0:0;na=squelette] chevaux squelettiques [/url].\n\nLes zones environnantes [zone=267], [zone=130], et la [zone=85] contiennent la plupart des quêtes pour gagner de la réputation auprès de Fossoyeuse.',NULL),(8,909,2,NULL,0,2,'La [b]Foire de Sombrelune[/b] est un mystérieux carnaval itinérant, qui parcourt non seulement Azeroth, mais aussi lOutreterre. Conduite par l\'inimitable [npc=14823], un gnome d\'héritage douteux et de racine inconnue. La Foire amène des jeux, des prix et des bibelots exotiques inattendus, puissants ou non, en [zone=215], à la [zone=12] ou à la [zone=3519] chaque mois.\n\nUne variété de divertissement est proposée par la Foire, mais l\'attraction la plus commune est la rédaction du billet. Plusieurs forains distribuent des [item=19182], répartis dans toute la Foire, ils offrent des bons contre des articles fabriqués par des travailleurs du cuir, des forgerons ou des ingénieurs ainsi que des objets rassemblés dans la nature tels que [item=11404] et [item=19933]. Les bons peuvent être échangés contre de nombreuses choses allant de la [item=19295] à des colliers de grande puissance.\n\nBeaucoup d\'aventuriers recherchent la Foire de Sombrelune pour trouver les mystiques [url=?items=15.0&filter=minle=1;cr=107;crs=0;crv=Combine+the+Ace]carte de Sombrelune[/url]. Les cartes de Sombrelune viennent en huit combinaisons, chacune ayant une suite de l\'As aux Huit. Avec la combinaison de toutes les cartes, la suite est créée qui commencera une quête pour vous envoyer à la foire de Sombrelune. \nChacune des huit suites produit un [url=?items=4.-4&filter=na=carte+sombrelune] bijou [/url] différent avec un effet différent, dont certains sont assez puissants.\n\nLe calendrier habituel de la Foire de Sombrelune arrive sur le site, le premier vendredi du mois et le départ commencera tôt le lundi suivant.',NULL),(8,76,2,NULL,0,2,'[b]Orgrimmar[/b] est la faction de la capital des orcs : [zone=1637]. Situé au bord nord de [zone=14], la ville imposante abrite le chef de guerre orcs, [npc=4949].\n\n[h3]Histoire[/h3]\n\nThrall a dirigé les orcs vers le continent de Kalimdor, où ils ont fondé une nouvelle patrie avec l\'aide de leurs frères tauren. En nommant leur nouvelle terre, Durotar, nom du père assassiné de Thrall, les orcs se sont installés pour reconstruire leur société autrefois glorieuse. La malédiction démoniaque sur leur race a pris fin, la Horde a décidé de passer dun discours de conquête avec une coalition lâche à la survie et à la prospérité pour tous. Aidé par les nobles Taurens et les Trolls rusés de la tribu Sombrelance, Thrall et ses orcs attendaient une nouvelle ère de paix dans leur propre pays.\n\nDe là, ils ont commencé la création de la grande ville guerrière, Orgrimmar. Nommé de l\'ancien chef de guerre, Orgrim [color=#ff143c]Doomhammer[/color], la nouvelle ville a été construite en peu de temps, à l\'aide des gobelins, des Taurens, des trolls et de [color=#ff122a]Mok\'Nathal Rexxar[/color]. En dépit d\'avoir des problèmes avec les centaures, les harpies, les lézards de tonnerre enragés, les kobolds, et malheureusement, l\'Alliance, Orgrimmar a prospéré et est devenu le foyer des orcs et des Trolls Sombrelance.\n\nAujourd\'hui, Orgrimmar se trouve à la base d\'une montagne entre Durotar et [zone=16]. Une ville guerrière en effet, elle abrite d\'innombrables quantités d\'Orcs, Trolls, Taurens, et une quantité croissante de Réprouvés rejoignent maintenant la ville, ainsi que les Elfes de Sang qui ont récemment été acceptés dans la Horde.\n\n[h3]Réputation[/h3]\n\n[npc=14726] offre une quête répétitive où il faut fournir des étoffes. Une fois exalté à Orgrimmar, en récompense, les joueurs peuvent acheter des[url=?items=15.5&filter=cr=93:92;crs=2:1;crv=0:0;na=Loup] loups [/url].\n\nLes zones environnantes Durotar et [zone=17] contiennent la plupart des quêtes pour gagner de la réputation avec Orgrimmar.',NULL),(8,530,2,NULL,0,2,'[b]Les Trolls Sombrelances[/b], tribu de Trolls exilés, ont uni leurs forces avec [npc=4949] et la Horde. Ils appellent maintenant [zone=1637] leur maison, qu\'ils partagent avec leurs alliés Orc. [npc=10540] est leur chef actuel.\n\n[h3]Histoire [/h3]\n\nLorsque les rivalités tribales ont éclaté dans l\'ancien Empire Gurubashi, la tribu Sombrelance s\'est trouvée chassée de sa patrie dans [zone=33]. S\'étant installés dans ce que l\'on croit aujourd\'hui être les îles brisées, la tribu se retrouve bientôt enchevêtrée dans un conflit avec une bande de murlocs. Leur sort semblait scellé jusqu\'à ce que Thrall, chef de guerre Orc, et son armée, nouvellement libérés, s\'emparent de leurs maisons. Contrôlée par une sorcière des mers, un groupe de murlocs a capturé le chef des Sombrelances, Sen\'jin, avec Thrall et plusieurs autres Orcs et Trolls. Thrall a réussi à se libérer avec d\'autres, mais n\'a finalement pas pu sauver le chef des Trolls. Bien que Sen\'jin ait été sacrifié par la sorcière des mers, il a pu révéler une vision qu\'il avait eu, dans laquelle Thrall conduirait les Sombrelances hors des îles.\n\nAprès son retour, Thrall et ses partisans ont réussi à repousser de nouvelles attaques de la sorcière des mers et de ses murlocs, et se sont à nouveau dirigés vers Kalimdor. Sous la direction de [npc=10540], les Sombrelances ont alors juré allégeance à la Horde de Thrall et les ont suivi. Maintenant considérés comme ennemis par toutes les autres tribus Trolls sauf les Vengebroches et les Zandalar, les Sombrelances sont aujourd\'hui méprisés. Pourtant, les Trolls Sombrelances n\'ont pas oublié quils ont été chassés de leurs terres ancestrales et cette animosité gardée est accentuée avec limpatience, surtout vers les autres tribus Trolls. Après avoir atteint la nouvelle patrie des Orcs, [zone=14], les trolls se sont alors installés sur les rives orientales du royaume Orc, les îles Echo.\n\nCependant, avec l\'arrivée de Kul Tiras et de sa marine, les Sombrelances ont été forcés de reculer à l\'intérieur des terres sous l\'assaut du commandant. Les Trolls, se battant avec la Horde aux côtés de leurs frères, ont vaincu l\'ennemi. Les Trolls ont alors réclamé leur nouvelle patrie. Peu de temps après, un sorcier du nom de [npc=3205] a commencé à utiliser la magie noire pour prendre possession de ses collègues Sombrelances. Au fur et à mesure que son armée de disciples augmentait, Vol\'jin ordonna que les trolls restant évacuent, alors Zalazane prit le contrôle des îles Echo. Les Sombrelances se sont installés sur la rive voisine, en nommant leur nouveau village en hommage à leur ancien chef Sen\'jin. Du village de Sen\'jin, ils envoient, avec leurs alliés, des forces pour combattre Zalazane et son armée asservie.\n\n[h3]Réputation[/h3]\n\n[npc=14727] offre une quête répétitive où il faut fournir des étoffes. Une fois exalté aux Trolls Sombrelances, en récompense, les joueurs peuvent acheter des [url=?items=15.5&filter=na=Raptor;cr=93:92;crs=2:1;crv=0:0] Raptors [/url].\nLa zone environnante, Durotar, contient la plupart des quêtes pour gagner de la réputation avec les Trolls Sombrelances. De plus, les joueurs de niveau supérieur ont également une bonne quantité de quêtes dans [zone=3521].',NULL),(8,92,2,NULL,0,2,'[b]Les Gelkis[/b] sont une tribu de centaures qui ont construit leur campement dans les parties les plus au sud de [zone=405]. Ce sont les ennemis mortels des [faction=93], une tribu de frère située également dans le sud de Desolace. Le chef fondateur, ou Khan, des Gelkis était [npc=13741], deuxième de la prétendue progéniture de Zaetar et Theradras. Ils sont actuellement dirigés par [npc=5602] et ont pour représentant [npc=5397].\nLes Gelkis ne tiennent aucune alliance avec leurs tribus de frères, mais sont aussi connus pour agir à la fois hostilement et passivement envers les membres de l\'Alliance comme de la Horde.\n\n[h3]Histoire[/h3]\n\nInitialement dirigé par le Second Khan Gelk, les Gelkis se situaient dans les régions les plus au sud de Desolace lorsque la tribu centaure se divisa en cinq.\nLorsque la tribu Gelkis s\'est prononcée contre le Khan Magra, une éternelle querelle entre les Magram et les Gelkis est née.\n\nLes Gelkis considérés comme plus civilisés que leurs frères avec une structure sociale organisée et une compréhension ferme de la langue commune, respectent la nature et leur mère de naissance Theradras. \nAlors que les Magram prônent la force comme essentielle et que la survie de la tribu dépend de leur esprit de combat.\n\nPour alléger ce conflit, Theradras veille toujours sur les centaures et gardera les tribus en sécurité et en vie. Les Gelkis ont alors demandé sa protection et donc le pouvoir de la terre maintien leur existence. \n\nBien que la Magram considère que cela soit faible, il semblerait que ce soit une vue erronée, car des élémentaires peuvent être aperçu dans Village Gelkis, mettant un terme aux intrus indésirables aux côtés de leurs maîtres centaures.\n\n[h3]Réputation[/h3]\n\nCest une des deux factions situées en Desolace, vous devez avoir une certaine réputation auprès des Gelkis pour commencer leurs quêtes. La réputation pour les Gelkis peut être obtenue en tuant les [url=?Npcs=7&filter=na=Magram]centaures Magram[/url].\n\nVous gagnez 20 points de réputation chez les Gelkis et perds 100 avec la tribu Magram.',NULL),(8,93,2,NULL,0,2,'[b]Les Magram[/b] sont une tribu de centaures qui construit leur campement dans les parties sud-est de [zone=405]. Ce sont les ennemis mortels de la [faction=92], une tribu de frère située également dans le sud de Desolace. Le chef fondateur, ou Khan, des Magram était [npc=13740], troisième de la prétendue progéniture de Zaetar et Theradras. Ils sont actuellement dirigés par [npc=5601] et ont pour représentant [npc=5398].\nLes Magram ne tiennent aucune alliance avec leurs tribus de frères, mais osont aussi connus pour agir à la fois hostilement et passivement envers les membres de l\'Alliance comme de la Horde.\n\n[h3]Histoire[/h3]\n\nÀ l\'origine menée par le troisième Khan Magra, les Magram se situaient contre les chaînes de montagnes de Desolace lorsque la tribu centaure se divisa en cinq.\nAvant la mort de Magra, il a installé l\'idée que la force était essentielle et que la survie de la tribu dépendait de son esprit de combat. Quand leur frère, la tribu Gelkis, s\'est prononcée contre cette notion, une éternelle querelle entre les deux tribus est née.\n\nLa poursuite de la force a continué à travers les Khans Magram jusqu\'à ce jour, transformant les centaures en des êtres violents et déterminés. Pour solidifier leur titre de plus fort, la tribu lutte encore férocement pour affaiblir ou détruire leurs clans de frères, considérant les Kolkar comme faible, les Gelkis comme une nuisance, et les Maraudon comme un formidable ennemi.\n\nOn peut supposer que la culture Magram s\'est développée autour de la force de culte avant tout. Par rapport aux Gelkis, les Magram tiennent des formes très primitives de la parole et de la structure sociale. Par exemple, leur compréhension commune est limitée et la position de Khan serait vraisemblablement recherchée par un démon de la mort.\n\n[h3]Réputation[/h3]\n\nC\'est une des deux factions situées à Desolace, vous devez avoir une certaine réputation auprès des Magram pour commencer leurs quêtes. La réputation pour les Magram peut être obtenue en tuant [url=?npcs=7&filter=na=Gelkis]les centaures Gelkis[/url]. \n\nVous gagnez 20 points de réputation chez les Magram et perds 100 avec la tribu Gelkis.',NULL),(8,270,2,NULL,0,2,'Les trolls de la[b] Tribu Zandalar[/b] sont venus à île de Yojamba dans la [zone=33] pour recruter de l\'aide contre le Dieu du sang ressuscité et ses prêtres d\'Atal\'ai dans [zone=19] et [zone=1417].\n\n[h3]Histoire[/h3]\n\nLes Zandalar étaient les premiers trolls connus, tribu d\'où provenaient toutes les tribus. Au fil du temps, deux empires troll distincts ont émergé, l\'Amani et le Gurubashi. Ils existaient pendant des milliers d\'années jusqu\'à l\'avènement des Elfes de la nuit, qui ont combattu avec eux et ont finalement conduit les deux empires à l\'exil.\n\nÀ la suite du Great Sundering, les Gurubashi vaincus sont de plus en plus désespérés. En cherchant un moyen de survivre, ils ont enrôlé l\'aide du sauvage [npc=14834], également appelé Soulflayer. Hakkar s\'est transformé en un oppresseur impitoyable qui a exigé des sacrifices quotidiens de ses sujets, les Gurubashi se sont alors retournés contre leur sombre maître. Les tribus les plus fortes (y compris les Zandalar) se sont regroupées pour vaincre Hakkar et ses fidèles prêtres, les Atal\'ai. Les tribus unies ont vaincu le Dieu des Sang et ont expulsé les Atal\'ai, et malgré leur victoire, l\'Empire Gurubashi tomba peu de temps après.\n\nAu cours des dernières années, les prêtres d\'Atal\'ai ont découvert que la forme physique de Hakkar ne peut être convoquée que dans la capitale ancienne et déserte de l\'Empire Gurubashi, Zul\'Gurub. Malheureusement, au cur de cette nouvelle quête, les prêtres ont invoqué, avec succès, Hakkar, confirmant la présence du Soulflayer redouté au cur des ruines.\n\nAinsi, la tribu Zandalar est arrivée sur les rives d\'Azeroth pour combattre encore Hakkar. Mais le dieu du sang est devenu de plus en plus puissant, pliant plusieurs tribus à sa volonté, et même, commandant les avatars des dieux primitifs: chauve-souris, panthère, tigre, araignée et serpent. Avec les tribus trolls éparpillées, les Zandalri ont été forcés de recruter des aventuriers de diverse origine d\'Azeroth pour les rejoindre dans la bataille, et espèrent une fois de plus vaincre, le Soulflayer.\n\n[h3]Réputation[/h3]\n\nLa réputation avec la tribu Zandalar est obtenue en tuant les monstres et boss dans Zul\'Gurub. Des quêtes répétitives et spécifiques sont aussi disponibles, elles requièrent des éléments qui ont été abandonnés dans linstance. Chaque Zul\'Gurub donne environ 2 500 à 3 000 de réputation.\nAvant la croisade brûlante, la principale raison de monter la réputation avec la tribu était les enchantements [url=?Items=0.6&filter=na=Zandalar]dépaule[/url], [url=?items=0.6&filter=minrl=60;maxrl=60;cr=18:107;crs=4:0;crv=0:to+a+leg+or+head+slot+item]de tête et de jambe[/url]. De plus, il y avait des pièces darmure en récompense de quête à faire dans Zul\'Gurub nécessitant un niveau de réputation.',NULL),(8,471,2,NULL,0,2,'[b]Les Marteaux-hardis[/b] sont un clan de nains actuellement centrés dans [zone=47] et la [zone=3520]. La faction a été supprimée dans le patch 2.0.1.\n\n[h3]Histoire[/h3]\n\nJuste avant le [objet=175739], le clan Marteaux-hardis, dirigé par Thane Khardros Marteaux-hardis, habitait les contreforts et les falaises autour de Forgefer. Le clan Marteaux-hardis a échoué à prendre le contrôle de [zone=1537], des clans Barbe-de-bronze et Sombrefer. Khardros et ses guerriers Marteaux-hardis se sont rendus au nord par les barrières de Dun Algaz et ont fondé leur propre royaume dans le lointain sommet de Grim Batol. Là, les Marteaux-hardis ont prospéré et reconstruit leurs richesses.\n\n[npc=9019] et ses Sombrefer ont juré de se venger de Forgefer. Thaurissan et sa femme sorcière, Modgud, ont lancé un attentat contre Forgefer et Grim Batol. les forces de Modgud ont commencé à franchir les portes de Grim Batol, elle a utilisé ses pouvoirs pour frapper la peur dans leurs curs. Les ombres se déplaçaient à son commandement, et des choses sombres se glissaient dans les profondeurs de la terre pour traquer les Marteaux-hardis dans leurs propres retranchements. Finalement, Modgud a franchi les portes et a assiégé la forteresse elle-même. Les Marteaux-hardis se sont battus désespérément, Khardros lui-même sest lancé dans la bataille pour tuer la sorcière reine. Avec leur reine perdue, les Sombrefer ont fui avant la fureur des Marteaux-hardis.\n\nUne fois que la menace immédiate des Sombrefer a été éliminée, les Marteaux-hardis sont rentrés à Grim Batol. Cependant, la mort du Modgud avait laissé une tache maléfique sur la forteresse de la montagne, et les Marteaux-hardis la trouvaient inhabitable. Khardros a conduit son peuple vers le nord vers les terres de Lordaeron. En s\'installant dans la région montagneuse des Hinterlands, et ces forêts luxuriantes, les Marteaux-hardis ont construit la ville de Nid-de-laigle, où les Marteaux-hardis se sont rapprochés de la nature et même liés aux puissants griffons de la région.\n\nLa menace la plus immédiate pour leurs sécurités vient de l\'est sous la forme de deux clans trolls, les Vilebranches et les Fanécorces. Ils sont les plus célèbres pour organiser des batailles contre la ville des Marteaux-hardis, tout en brandissant des armes puissantes.\nLes nains Marteaux-hardis ont un certain nombre de clans, chacun gouverné par un Thane. Le plus fort Thane règne sur Nid-de-laigle.',NULL),(8,509,2,NULL,0,2,'[b]La Ligue d\'Arathor[/b] a été initialement établie par les survivants du Royaume de Stromgarde pour récupérer la [zone=45] des mains des Profanateurs au Trépas d\'Orgrim. Aujourd\'hui, c\'est une organisation à l\'appui de l\'Alliance, basée sur [zone=3358] dans le Refuge de lOrnière. Ils se sont chargés d\'aider à fournir des forces, pour l\'Alliance, lorsque cest nécessaire, leurs membres incluent toutes les races de l\'Alliance mais se sont encore principalement des humains stromgardiens.\n\n[h3]Réputation[/h3]\n\nLes joueurs peuvent gagner la réputation dans cette faction en participant au champ de bataille du bassin Arathi. Lorsque vous vous battez dans le bassin d\'Arathi, vous gagnez 10 points de réputation pour 160 ressources. Sur les weekends d[event=20], les ressources requises sont ramenées à 150.\n\nOn vous accorde le titre, [title=48], une fois exalté avec Ligue dArathor et les deux autres factions du champ de bataille, [faction=890] et [faction=730].',NULL),(8,730,2,NULL,0,2,'[b]Les Gardes Foudrepiques[/b] est la faction de l\'Alliance dans le champ de bataille [zone=2597]. Ils sont une expédition de nains du clan Foudrepique, originaire des « vallées d\'Alterac » dans [zone=36]. La recherche des Foudrepiques pour les reliques de leurs passés et la récolte de ressources dans la vallée d\'Alterac ont conduit à une guerre ouverte avec les Orcs de la [faction=729] habitant dans la partie sud de la vallée. Ils ont également reçu un « ordre de la souveraineté impérialiste » par [npc=2784] pour prendre les vallées d\'Alterac pour [zone=1537].\n\nLa principale base des Foudrepiques est Dun Baldar, où son chef, [npc=11948], réside avec ses maréchaux. Son second commandant, [npc=11949], se trouve au sud de Dun Baldar, à Cur de pierre.\n\n[h3]Réputation[/h3]\n\nLes joueurs peuvent gagner leurs réputation, dans cette faction, en participant au champ de bataille de la vallée dAlterac, en faisant diverses tâches et en tuant les membres de la faction adverse, le clan Frostwolf.\n\nOn vous accorde le titre : [title=48] au joueur, une fois quil est exalté avec les Gardes Foudrepiques et les deux autres factions des champs de bataille, [faction=890] et [faction=509].',NULL),(8,510,2,NULL,0,2,'[b]Les Profanateurs[/b] cherchent à feuilleter la [faction=509] dans le champ de bataille, [zone=3358]. Aujourd\'hui, c\'est une organisation à l\'appui de la Horde, basée au Trépas dOrgrim dans [zone=45]. Ils se sont investis pour aider les forces de la Horde, au besoin, et leurs membres incluent toutes les races de la Horde, même si, se sont encore principalement des Orcs.\n\n[h3]Réputation[/h3]\n\nLes joueurs peuvent gagner la réputation dans cette faction en participant au champ de bataille du bassin Arathi. Lorsque vous vous battez dans le bassin d\'Arathi, vous gagnez 10 points de réputation pour 160 ressources. Sur les weekends d[event=20], les ressources requises sont ramenées à 150.\n\nOn vous accorde le titre, [title=48], une fois exalté avec les Profanateurs et les deux autres factions du champ de bataille, [faction=889] et [faction=729].',NULL),(8,529,2,NULL,0,2,'L[b]Aube dArgent[/b] est une organisation axée sur la protection d\'Azeroth des menaces qui cherchent à la détruire, comme la Légion Ardente et le Fléau. Les forteresses de l\'Aube d\'Argent se trouvent dans les [zone=139] et les [zone=28]. Elle maintient également une présence dans [zone=1657] et dans les [zone=85], et dans dautres zones moins remarquables. La réputation avec lAube dArgent peut être utilisée pour acheter divers plans, consommables, et pour atténuer le coût à [zone=3456]. Avec l\'expansion « Burnning Croisade », la réputation de lAube dArgent a diminué en valeur.\n\nLe [item=22999] a pour icône un lever de soleil argenté.\n\n[h3]Histoire[/h3]\n\nAprès la mort du [npc=16062], la corruption de la Croisade Écarlate est devenu évidente pour certains de ses membres, qui ont par la suite abandonné les rangs de la [url=?npcs&filter=na=croisade%20écarlate;ex=on]Croisade Écarlate[/url] et a créé lAube dArgent pour protéger Azeroth de la menace du Fléau sans présence de fanatique dans la Croisade Écarlate.\n\nAlors qu\'ils partagent les mêmes objectifs que la Croisade, lAube dArgent a ouvert ses rangs non seulement aux races de l\'Alliance, mais aussi aux membres de la Horde et même à certains des Réprouvés. Ils mettent en garde contre la discrétion et l\'introspection, et mettent beaucoup l\'accent sur la recherche du Fléau et sur la façon de le combattre.\n\nAvec le temps, lAube dArgent s\'est diversifié, comme le Fléau qui s\'est divisé de nouveau, avec un rejeton appelé la Fraternité de la Lumière, un compromis entre l\'approche plus savante de lAube dArgent et le fanatisme de la Croisade écarlate.\n\n[h3] Réputation [/h3]\n\n[b]Les pierres du Fléau[/b]\nTout en portant un bijou accordant l\'effet « Commission pour lAube dArgent », les personnages peuvent tuer des monstres mort-vivants pour leurs [url=?items=12&filter=cr=151;crs=6;crv=43169;na=pierre%20du%20fléau] pierres du Fléau[/url] et ensuite les transformer en monnaies échange contre [item=12844]. Les quêtes requièrent beaucoup de [item=12843], [item=12841] et [item=12840]. Il convient de noter que les monnaies déchanges reçus des entités doivent être sauvegardés jusqu\'à ce que le statut de Révéré soit atteint, car les quêtes ne donneront plus de réputation après.\n\nUne autre façon daugmenter la réputation avec lAube dArgent est de faire la quête répétable « Chaudron ». Les chaudrons sont une source de « production » de membres du Fléau.\n\nComme la plupart des factions, le joueur peut faire des instances pour augmenter sa réputation. Les instances associées sont [zone=2017] et [zone=2057]. Naturellement, ces instances incluent également des quêtes qui augmentent la réputation de lAube dArgent.',NULL),(8,933,2,NULL,0,2,'[b]Le Consortium[/b],dirigé par [npc=19674], sont des passeurs éthérés, des commerçants et des voleurs qui sont venus en Outreterre. Le principal base d\'opérations et le plus grand rassemblement se trouve à Foudreflèche, mais ils peuvent être trouvés à[color=#ff0537] Midrealm Post[/color], Aeris Landing, près d\'Auchindoun à [zone=3792] et dans d\'autres endroits.\n\nEn arrivant à un statut amical, les joueurs sont officiellement considérés comme membres du Consortium et bénéficient d\'un salaire. Le salaire est un sac de gemmes au début de chaque mois, donné par [npc=18265] chez Aeris Landing. Une plus grande réputation avec le Consortium produit des qualités et quantités supérieures de gemmes chaque mois.\n\n[h3]Réputation[/h3]\n\n[b]Jusqu\'à Amical[/b]\n[ul]\n[li]Faire le donjon Tombe-mana en [i]mode normal[/i] rapporte environs 1 200 points de réputation[/li]\n[li]Donner des [item=25416] à [npc=18265].[/li]\n[li]Donner des [item=25463] à [npc=18333].[/li]\n[/ul]\n\n[b]De amical à honoré[/b]\n[ul]\n[li]Faire Tombe-mana en [i]mode normal[/i] rapporte environs 1 200 point de réputation.[/li]\n[li]Activer les [item=25433] à [npc=18265].[/li]\n[li]Donner des [item=29209] à [npc=19880].[/li]\n[/ul]\n\n[b]De honoré à exalté[/b]\n[ul]\n[li]Faire Tombe-mana en [i]mode héroïque[/i] rapporte environs 2 400 points de réputation.[/li]\n[li]Faire toutes les [url=?Quêtes et filtre=cr=1;crs=933;crv=0]quêtes[/url].[/li]\n[li]Donner des [item=25433] à [npc=18265].[/li]\n[li]Donner des [item=29209] à [npc=19880].[/li]\n[/ul]\n\nToutes personnes qui essayent de gagner simultanément la réputation du Consortium et des [faction=941] ou [faction=978] peuvent se concentrer à tuer des ogres ([url=?npcs&filter=na=rochepoing;cr=6;crs=3518;crv=0]Rochepoing[/url], [url=?npcs&filter=na=cogneguerre;cr=6;crs=3518;crv=0]Cogneguerre[/url]) à Nagrand et rendre les perles de guerre obsidienne au Consortium.\n\nLa seule mise en garde est le taux de loot, soit environ 33% pour les Cogneguerre, alors qu\'il est de 50% pour les insignes. Si vous êtes au niveau 70 et que vous voulez monter cette réputation plus rapidement sans se soucier de la réputation de Mag\'har / Kurenai, vous voudrez peut-être donner des insignes à la place. Ensuite, les ogres sont généralement plus faciles à tuer, allant du niveau 65 à 67. Le choix dépend finalement du joueur.',NULL),(8,932,2,NULL,0,2,'[b]L\'Aldor[/b] est un ancien ordre de prêtres draeneïs qui vénèrent les naaru, et à ce jour ils assistent les naaru [faction=935] dans leur combat contre [npc=22917] et la Légion Ardente. Ils se trouvent principalement dans la [zone=3520] et [zone=3703]. Bien qu\'ils aient beaucoup souffert des Elfes du sang qui sont devenus [faction=934], ont mis de côté une guerre ouverte contre les Sha\'tar. Le temple le plus saint de l\'Aldor repose sur léminence de l\'Aldor, surplombant la ville à l\'ouest.\n\nLa plupart des joueurs commenceront à une réputation neutre auprès de l\'Aldor. [npc=18166] à Shattrath donnera aux joueurs une première quête pour devenir amical avec Aldor ou Les clairvoyants. Ce choix est réversible si les joueurs ressentent le besoin.\nLes joueurs de Draenei seront directement amicaux avec Aldor et hostiles avec les Clairvoyants, alors que les joueurs Elfe du sang seront hostiles à l\'Aldor et amicaux envers les Clairvoyants.\n\n[npc=19321] et [npc=20807] sont situés dans la banque Aldor, sur le bord nord de la terrasse de la lumière. Le sanctuaire de la lumière sans fin sur léminence de l\'Aldor abrite [npc=20616] [petit][/small] et [npc=21906] [petit][/small], qui échangent, respectivement, des jetons épiques d\'armure contre des pièces de set de [url=?Itemsets&filter=ta=12]Niveau 4[/url] et de [url=?Itemsets&filter=ta=13]Niveau 5[/url].\n\n[i]Note : Les gains de réputation avec Aldor correspondent à une perte de réputation de 10% plus élevée chez les Clairvoyants. La plupart des gains de réputation avec Aldor accorderont également 50% de la réputation avec le Sha\'tar.[/i]\n\n[h3]Réputation[/h3]\n\n[b]Jusqu\'à honoré[/b]\n\nLes joueurs qui cherchent à gagner les rangs de réputation supérieurs (Révéré, Exalté) peuvent vouloir sauver des quêtes non répétables jusqu\'à ce qu\'ils soient honorés.\n\nDonner 10 [span class=q1][item=29425][/span] à [npc=18537] dans léminence de l\'Aldor accordera 250 points de réputation pour l\'Aldor. Il existe également une quête répétable où donner une unique marque accorde 25 points de réputation. Ces marques tombent sur des membres inférieurs de la Légion Ardente trouvés dans la plupart des zones de Outreterre, y compris les deux camps au nord d\'Auchindoun dans les déchets osseux de [zone=3519].\nEnviron 240 marques sont nécessaires pour passer d\'amical à honoré.\nEn outre, ces quêtes fournissent de la réputation de Sha\'tar ; 125 points de réputation pour 10 marques ou 12,5 points de réputation pour une unique marque.\n\nLes joueurs qui souhaitent également faire la réputation des factions [faction=978] ou [faction=941] iront tuer des Orcs à la forteresse de Kil\'Sorrow dans le sud-est de [zone=3518], car ils donnent des marques ainsi que 10 points de réputation auprès des Kurenai ou des Mag\'har.\n\n[b]Jusqu\'à Exalté[/b]\n\nUne fois que vous atteignez le niveau 68, vous pouvez également donner 10 [span class=q1] [item=30809][/span], c\'est le même principe que les marques de Kil\'jaeden mais ceux-ci tombent sur des partisans de haut rang de la Légion Ardente. Si vous le souhaitez, vous pouvez transformer les marques de niveau supérieur avant la réputation honorée. Dans [zone=3522], la porte de la mort dispose du plus grand nombre de membre avec ce grade.\n\n[b]Arme gangrenée[/b]\n\n[span class=q2][item=29740][/span] peut être donné à tout moment à [npc=18538] [small][/small] à léminence de l\'Aldor. Cela augmentera votre réputation avec l\'Aldor de 350 par arme gangrenée.\nEn plus des gains de réputation, vous recevrez [span class=q1][item=29735][/span], qui est la condition pour acheter lenchantement d\'épaule à [npc=20807] dans la banque de l\'Aldor.\n\n[h3]Passer à la réputation de l\'Aldor[/h3]\n\nPour changer votre faction des Claivoyants vers l\'Aldor et donc pour accéder à leurs recettes d\'artisanat (et annuler toutes les réputations que vous avez faites), trouvez [npc=18597], un membre de l\'Aldor dans la ville basse. Elle propose une quête répétable où pour 8x [span class=q1][item=25802][/span] vous montez la réputation Aldor. Une fois que vous êtes neutre, vous ne pourrez plus recevoir cette quête.',NULL),(8,922,2,NULL,0,2,'[b]Tranquilliens[/b] a été reprise par les Réprouvés et les Elfes de sang puis est devenu une faction des [zone=3433].\n\n[h3]Histoire[/h3]\n\nAlors que l\'armée du Fléau faisait son chemin vers le Puit-du-Soleil, les elfes n\'avaient pas d\'autre choix que de se retirer, Tranquillien fût donc abandonnée. La ville est maintenant utilisée par les Elfes de sang et les Réprouvés comme base d\'opération pour lancer des attaques visant à reprendre les Terres Fantômes. Cependant, la ville est entourée par le fléau, même les courriers ont du mal à traverser l\'ennemi pour atteindre la ville. Les forces mortels de Mortholme sont la menace la plus dangereuse pour la ville.\n\n[h3]Réputation[/h3]\n\nContrairement à la plupart des zones de départ, la ville de Tranquillien a sa propre faction.\nToutes les quêtes que vous effectuez pour eux accumuleront au moins 1000 points de réputation. [npc=16528] agit comme lintendant des Tranquilliens. Vredigar peut être trouvé près de l\'auberge et vendra divers éléments [span class=q2]commun[/span], et même un manteau [span class=q3]rare[/span] lorsque vous atteignez la réputation exaltée.\n\nSi vous complétez toutes les quêtes des Tranquilliens, vous devriez être exalté.\nIl existe une variété de quêtes concernant principalement la récupération des villages envahis, l\'enquête sur les morts-vivants et l\'aide apportée à la population. La suite de quête prend « fin » avec la quête où il faut tuer [npc=16329].',NULL),(8,910,2,NULL,0,2,'La [b]Progéniture de Nozdormu[/b] est une faction composée du vol Draconique de bronze. Leur chef, [npc=15192], se trouve à l\'extérieur des [b]Grottes du temps[/b], avec beaucoup de ses agents volant dans le ciel de [zone=1377].\n\nPour ouvrir les portes d[b]Ahn\'Qiraj[/b], un champion doit compléter une longue ligne de quête pour le dragon de bronze Anachronos. Cette réputation est également présente dans [zone=3428]; Elle permet dobtenir des équipements et des bagues épiques.\n\n[h3]Réputation [/h3]\n\nLes joueurs commencent leur réputation au plus bas niveau possible, cestà-dire 0/36000 de détestés.\n\nLa réputation de la Progéniture de Nozdormu peut être gagnée en tuant des monstres à l\'intérieur du temple d\'Ahn\'Qiraj et en faisant des quêtes liées. Vous pouvez également exploiter [item=20384], cela prend beaucoup plus de temps et nécessite l\'obtention de [item=20383] dans [zone=2677] pour la suite de quête [item=21175].\n\nTuer des monstres dans le temple d\'Ahn\'Qiraj ne permet que datteindre une réputation de 2999/3000 de neutre, la réputation ne peut donc être avancée que par des quêtes et la remise de [item=21229] et [item=21230]. \nUn conseil, gardez tous les insignes jusqu\'à ce que vous soyez à une réputation neutre, car à ce moment-là, cela devient beaucoup plus difficile.',NULL),(8,749,2,NULL,0,2,'Les [b]Hydraxiens[/b] sont des élémentaires qui se sont installés sur les îles à l\'est de [zone=16]. Les ennemis jurés des armées de [npc=11502]. Historiquement serviteurs des Anciens Dieux, les quatre Lords Élémentaires ont servi les dieux avec une loyauté éternelle. Les minions de Neptulon, le chasse-marée, étaient nombreux et insensés. On ne sait pas encore comment le [npc=13278] a libéré le contrôle de son seigneur ou quels sont ses objectifs ultimes, mais les élémentaires deau sont les seuls éléments qui n\'attaquent pas les races mortelles.\n\nSitué sur une île éloignée dans l\'extrême est d\'Azshara, le Duke Hydraxis propose des quêtes. Les deux premiers nécessitent de tuer divers élémentaires dans les [zone=139] et en [zone=1377]. Une réputation accrue avec les Hydraxiens ouvre des quêtes supplémentaires menant à [zone=2717]. Tous les objets obtenus auprès des Hydraxiens sont gagnés à partir de différentes missions.\n\nL\'achèvement de la suite de quête permet aux joueurs d\'obtenir [item=17333] utilisé pour endommager les runes trouvées près de la plupart des boss dans Cur de Magma. Ceci est nécessaire pour convoquer [npc=12018], l\'avant-dernier boss, et, après sa défaite, pour convoquer Ragnaros lui-même. Comme il y a sept runes, tout raid nécessite au moins sept joueurs qui apportent une quintessence s\'ils souhaitent terminer l\'instance. Comme la majeure partie de la suite de quête a lieu au sein de Cur de Magma, toutes personnes du raid peuvent compléter cette tâche avec un peu plus que quelques voyages et une course au [zone=1583].\n\n[h3] Réputation [/h3]\n\nLa réputation des Hydraxiens est obtenue en tuant les ennemis élémentaires suivants :\n[ul][li] [npc=11746] - 5 points de réputation, jusqu\'à l\'Honoré. [/li]\n[li] [npc=11744] - 5 points de réputation, jusqu\'à Honoré.[/li]\n[li] [npc=7032] - 5 points de réputation, jusqu\'à Honoré.[/li]\n[li] [npc=9017] - 15 points de réputation, jusqu\'à Révéré. [/li]\n[li] [npc=14478] - 25 points de réputation, jusqu\'à Révéré. [/li]\n[li] [npc=9816] - 50 points de réputation, jusqu\'à Révéré. [/li]\n[li] [npc=11658], [npc=11673], [npc=12101] et [npc=11668] - 20 points de réputation, jusqu\'à Révéré. [/li]\n[li] [npc=11659], [npc=12100], [npc=12076], [npc=11667] et [npc=11666] - 40 points de réputation, jusqu\'à Révéré. [/li]\n[li] [npc=12118], [npc=11982], [npc=12259], [npc=12057], [npc=12056], [npc=12264] et [npc=12098] - 100 points de réputation, jusqu\'à Exalté. [/li]\n[li] [npc=11988] - 150 points de réputation, jusqu\'à Exalté. [/li]\n[li] [npc=11502] - 200 points de réputation, jusqu\'à Exalté. [/li][/ul]\n\nLa réputation au statut de Révéré avec les Hydraxiens permet aux joueurs dobtenir le [item=22754], qui se recharge. Et donc évite la nécessité de retourner à Hydraxis pour obtenir une nouvelle quintessence chaque semaine.',NULL),(8,609,2,NULL,0,2,'Le [b]Cercle Cénarien [/b] est une organisation de druides, à la fois tauren et elfe de nuit, nommé d\'après Cénarius. Ses membres se consacrent à la protection de la nature et à la restauration de celle-ci suite aux dégâts subis par des forces malveillantes.\n\nLe Cercle a de nombreux sites, mais leur ville principale est la ville de Havre- nuit dans la [zone=493]. Les druides apprennent le sort [sortilège=18960] au niveau 10, mais il est aussi possible dy arriver par [zone=361] via le tunnel des Grumegueles.\n\nLe cercle Cénarien est aussi beaucoup présent en [zone=1377], où ils combattent les Silithides, les Qirajis et larmée du crépuscule. Le repos du vaillant et le Fort Cénarien servent de base dans ces terres hostiles et offrent de nombreuses opportunités aux aventuriers qui cherchent à aider les druides.\n\n[h3]Membres notables[/h3]\n\n[ul][li][npc=11832], fils de Cenarius [/li]\n[li][npc=3516], chef des druides - elfes de la nuit [/li]\n[li][npc=5769], chef des druides - Taurens [/li][/ul]\n\n[h3]Réputation[/h3]\n\nIl existe plusieurs façons de se faire connaître avec le cercle Cénarien.\nMise à part les [url=?Quests&filter=cr=1;crs=609;crv=0]quêtes[/url], vous pouvez faire ce qui suit pour gagner en réputation: \n[ul]\n[li]Le raid des [zone=3429] est de loin le moyen le plus rapide de gagner en réputation, car un clean complet peut dépasser 2000 points de réputation. [/li]\n[li] Tuez larmée du crépuscule. Elle cesse daugmenter une fois que vous atteignez la réputation Honoré pour [npc=11880] et [npc=11881], et Révéré pour [npc=15201].[/li]\n[li] Trouvez des [item=20404 ]. Ceux-ci se trouvent sur larmée du crépuscule et produisent 250 points de réputation pour 10 textes.[/li]\n[li] Trouvez des [item=20513], [item=20514] et [item=20515]. Ceux-ci se trouvent sur les mini-boss qui sont convoqués aux pierres de vent en utilisant [itemset=492]. [/li]\n[li] Effectuez la quête : [quest=8507]. Ce sont soit des [url=?search=logistique+Briefing] Quêtes de logistique [/url], des [url=?search=combat+Briefing]quêtes de Combat[/url] ou des [url=?search=tactique+Briefing] Quêtes tactiques [/url]. Les badges que vous gagnez de ces quêtes peuvent être transformés en réputation supplémentaire, si vous choisissez d\'abandonner les récompenses. [/li]\n[li] Collectez les [object=181598] de la zone et rendez les à votre faction.[/li]\n[/ul]',NULL),(8,589,2,NULL,0,2,'Les [b]Éleveurs de sabres-d\'hiver[/b] est une faction de l\'Alliance composée de deux Elfes de la nuit qui peuvent être trouvés au [zone=618]. À l\'heure actuelle, le seul donneur de quête est [npc=10618], qui est situé au sommet du Rocher des Sabres-d\'hiver au Berceau-de-lhiver. En atteignant un niveau de réputation exalté avec cette faction, Rivern vendra une monture spéciale, le [item=13086].\n\nLa monture de cette faction est la seule monture épique, ayant une vitesse de 100%, utilisable avec une compétence en équitation de 75. La faction est connue pour ne pas avoir déquivalant côté Horde et être la plus longue et la plus répétitive des réputations à monter dans l\'ensemble du jeu. La première quête peut être faite au niveau 58, tandis que les deux autres sont réalisables quau niveau 60.\n\n[h3]Réputation[/h3]\n\nLa réputation avec les Éleveurs de sabres-d\'hiver ne peut être obtenue que par trois quêtes répétables. Il n\'y a pas d\'objets de faction ni de mobs qui récompensent la réputation directement.\n\n[b]De neutre 0 à 1500[/b]\n\nUne seule quête répétable sera disponible jusqu\'à ce quune réputation de 1500/3000 soit atteinte, la quête : [quest=4970] doit donc être répétée. Tous les [url=?npcs&filter=cr=6;crs=618;crv=0;na=Croc%20acéré]Ours[/url] et [url=?npcs&filter=cr=6;crs=618;crv=0;na=Noroît]Noroît[/url] au Berceau-de-lhivers peuvent looter les objets de quête. Cette quête doit être effectuée en solo, car les taux de loot sont faibles et ne sont pas partageables si d\'autres ont la quête.\n\n[b]De neutre 1500 à exalté [/b]\n\nÀ mi-chemin du neutre, la quête : [quest=5201] sera disponible. Cette quête nécessite de tuer 10 Tombe-hivers dans le village Tombe-hivers, juste à l\'est de Long-guet. Si la quête : [quest=8464] a été effectuée pour [faction=576], les [item=21383] peuvent tomber sur les Tombe-hivers. Si un joueur veut les deux réputations, il préférable quil les gardes jusquà ce quil soit Révéré avec les Grumegueules. Ce qui entraînera beaucoup de réputation \"gratuite\".\n\nCette quête peut se faire en groupes pour aller plus vite. Les joueurs qui augmentent les réputations des Éleveurs de sabres-d\'hiver et des Grumegueules peuvent être trouvés dans le village des Tombe-hivers. Même en épique, le voyage vers le village Tombe-hivers prend beaucoup de temps. Il y a des tigres sur la route qui vous étourdiront, ce qui entraînera un désarçonnement, cela devrait être évité (mais peut être difficile car ils vont vous rattraper sur une monture de 60%). \n\n[b]De honoré à exalté[/b]\n\nA partir dhonoré, la troisième quête : [quest=5981] est disponible. La quête exige que le joueur tue 8 géants. Ils sont beaucoup plus difficiles que les Tombe-hivers et le trajet est assez long. Cette quête est généralement ignorée.\n\nEn raison de certains joueurs qui augmentent la réputation des Grumegueules, dans le village de Tombe-hivers, cette quête peut effectivement se révéler une récompense de réputation plus rapide que [quest=5201].',NULL),(8,576,2,NULL,0,2,'[b]Les Grumegueules[/b], dernière tribu furbolg non-corrompue (au moins dans leur point de vue), cherchent à conserver leurs voies spirituelles et à mettre fin à la souffrance de leurs frères.\n\nLes Grumegueules habitent deux zones : [zone=16] et [zone=361]. Ils sont présumés être la seule tribu furbolg à échapper à la corruption démoniaque, mais ce n\'est peut-être pas vrai, en raison de l\'existence de [npc=3897], furbolg de tribu inconnue, et la tribu Stillpine sur [zone=3524]. Cependant, de nombreuses autres races tuent les furbolgs aveuglément maintenant, sans savoir si elles sont alliées ou non. Pour cette raison, les Grumegueles ne se montrent pratiquement pas.\n\nLes aventuriers qui recherchent les Grumegueules dans le nord de Gangrebois et s\'aventurent chez eux apprendront quil faut mieux être leurs alliés. Bien qu\'ils ne possèdent pas de bijoux fins ou de richesses mondaines, la tradition chamanique des Grumegueules est encore forte. Ils connaissent bien l\'art de fabriquer des armures à partir de peaux d\'animaux, et ils sont plus qu\'heureux de partager leurs connaissances de guérison avec des amis de leur tribu. En outre, à partir dune réputation inamical, les Grumegueules vous accorderont également un accès sans problème à [zone=493] et [zone=618] dans leurs tunnels.\n\n[h3] Réputation[/h3]\n\nLa réputation avec la faction des Grumegueules est principalement acquise grâce à des quêtes. Les membres de la tribu Mort-bois, une autre tribu de Furbolg à Gangrebois, sont les principaux ennemis des Grumegueules et peuvent être tué pour gagner de la réputation.\n\n[ul]\n[li] Tuer des furbolgs [url=?Npcs&filter=na=Tombe-hivers]Tombe-hivers[/url] ou [url=?Npcs&filter=na=Mort-bois]Mort-bois[/url], donne 10 points de réputation. Les gains s\'arrêtent à révéré. [/li]\n[li] Tuer [npc=9464] ou [npc=9462], donne 60 points de réputation.[/li]\n[li] Tuer [npc=10738], située dans une grotte à l\'est de [faction=577], donne 50 points de réputation. Son taux de réapparition est de 6 à 8 minutes. [/li]\n[li] Tuer [npc=14342], élite rare, donne 50 points de réputation. Il se situe au village des Mort-bois à Gangrebois. Donne de la réputation jusquà exalté. [/ Li]\n[li] Tuer [npc=10199], élite rare, donne 50 points de réputation. Il se situe dans le village des Tombe-hivers au Berceau-de-lHivers. Donne de la réputation jusquà exalté. [/li]\n[li] Après avoir terminé la quête : [quest=8460], avec les [item=21377] ramassés sur les Furbolgs Mort-bois, la réputation augmente de 150 points. [/li]\n[li] Après avoir terminé la quête : [quest=8464], avec les [item=21383] ramassés sur les furbolgs Tombe-hivers, la réputation augmente de 150 points.[/li]\n[/ul]',NULL),(8,890,2,NULL,0,2,'[b]Les Sentinelles d\'Aile-argent[/b] représente la faction de l\'Alliance sur le champ de bataille [zone=3277]. Les elfes de la nuit, qui ont commencé une avancée massive pour reprendre les forêts de [zone=331], concentrent leur attention sur le débarquement sur leur terre de la [faction=889] une fois pour toutes. Et ainsi, les Sentinelles d\'Aile-argent ont répondu à l\'appel et ont juré qu\'ils ne vont pas se reposer avant que tous les orcs soient vaincus et expulsés du Goulet des Chanteguerres.\n\n[h3]Réputation[/h3]\n\nLes joueurs peuvent gagner leurs réputations, dans cette faction, en participant au champ de bataille du Goulet des Chanteguerres. Vous gagnez 35 points de réputation à chaque fois que votre faction capture un drapeau. Ce gain de réputation est augmenté à 45 les week-ends du champ de bataille.\n\nOn vous accorde le titre : [title=47] une fois quil est exalté avec Les Sentinelles d\'Aile-argent et les deux autres factions des champs de bataille, [faction=730] et [faction=509].',NULL),(8,889,2,NULL,0,2,'[b]Les Voltigeurs Chanteguerre[/b] est un clan orc précédemment dirigé par [npc=18076], daprès lequel le clan a été nommé. Les Voltigeurs Chanteguerre représentent la faction de la Horde sur le champ de bataille [zone=3277], où ils tentent de défendre leurs opérations d\'enregistrement dans [zone=331] de la [faction=890].\n\nCest l\'un des clans les plus forts et les plus violents, le clan de Chanteguerre était également l\'un des clans les plus distingués de Draenor, ce clan a pu échapper aux forces de l\'expédition de l\'Alliance à chaque tournant. Formés comme Grunts, ils ont maîtrisé l\'utilisation d\'épées et de lames et quelques-uns ont même atteint le rang de Maître-lames.\n\n[h3]Réputation[/h3]\n\nLes joueurs peuvent gagner leurs réputations, dans cette faction, en participant au champ de bataille du Goulet des Chanteguerres. Vous gagnez 35 points de réputation à chaque fois que votre faction capture un drapeau. Ce gain de réputation est augmenté à 45 les week-ends du champ de bataille.\n\nOn vous accorde le titre : [title=47] une fois quil est exalté avec Les Voltigeurs Chanteguerre et les deux autres factions des champs de bataille, [faction=510] et [faction=729].',NULL),(8,729,2,NULL,0,2,'[b]Le Clan Loup-de-givre[/b], ainsi que [npc=11946], ont vécu dans [zone=36] et ont des Loups de givre comme compagnons. Des nains, connue sous le nom de [faction=730], ont commencé une expédition dans le territoire des Loup-de-givre pour creuser la vallée et miner les veines. Une transgression envers les Orcs qui habitaient en Alterac. Cela a provoqué lextermination de la première expédition et la bataille pour [zone=2597] a commencé.\n\n[h3]Réputation[/h3]\n\nLes joueurs peuvent gagner leurs réputation, dans cette faction, en participant au champ de bataille de la vallée dAlterac, en effectuant diverses tâches et en tuant les membres de la faction opposée, les Gardes Foudrepiques.\n\nOn vous accorde le titre : [title=47] au joueur une fois quil est exalté avec le clan Loup-de-givre et les deux autres factions des champs de bataille, [faction=889] et [faction=510].',NULL),(8,935,2,NULL,0,2,'[b]Les Sha\'tar[/b], ou \"né de la lumière\", sont des naaru qui ont aidé [faction=932], l\'ordre des prêtres draenei précédemment dirigés par [npc=17468], en reconstruction à [zone=3703]. La ville a été détruite par les Orcs pendant leur fuite à travers Draenor avant la Première Guerre mondiale. \nLa défaite de la Légion ardente est le but ultime des Sha\'tar. Les Sha\'tar sont aidés dans cette guerre par l\'Aldor et leurs rivaux, la faction des elfes du sang connue sous le nom : [faction=934]. \nL\'Aldor et les Clairvoyants se battent pour la faveur du Sha\'tar afin qu\'ils puissent être aidés dans leur guerre pour les pouvoirs des naaru. L\'entité qui dirige le Sha\'tar est connue sous le nom de [npc=18481] ; Il peut être trouvé sur la terrasse de la lumière dans la ville de Shattrath.\n\nLes joueurs de l\'Alliance et de la Horde commencent avec une réputation neutre auprès des Sha\'tar. Les joueurs peuvent augmenter leur réputation, Sha\'tar, à travers diverses quêtes, en élevant leur réputation avec lAldor ou les clairvoyants, ou en s\'aventurant dans le [url=?search=donjon+tempête]donjon des tempêtes [/url].\n\n[h3]Réputation[/h3]\n\n[b]Jusqu\'à honoré[/b]\n\nLa réputation peut être obtenue à partir de divers objets. Ce qui suit n\'accordera que de la réputation de Sha\'tar jusqu\'à ce que vous obteniez un statut honoré : \n[li]Pour une réputation envers les Clairvoyants : [item=29426], [item=30810] et [item=29739][/li]\n[li]Pour une réputation envers l\'Aldor : [item=29425], [item=30809] et [item=29740][/li]\n\n[i]Notez que ce gain de réputation ne s\'affiche pas dans le journal de combat, mais peut être vérifié en regardant votre panneau de réputation.[/i]\n\nLa réputation peut également être obtenue en faisant le temple des tempêtes : [zone=3847], [zone=3846] et [zone=3849].\n\n[b]Jusquà exalté [/b]\n\nAprès avoir épuisé les récompenses de réputation de Aldor ou des Clairvoyants, les joueurs souhaiteront peut-être compléter les quelques quêtes de Sha\'tar disponibles. En plus des quêtes, les instances qui se trouvent au temple des tempêtes : Botanica, Arcatraz et Mechanar continueront à accorder de la réputation. À ce stade, il est probablement plus utile d\'exécuter ces instances en mode héroïque.',NULL),(8,934,2,NULL,0,2,'[b]Les Clairvoyants[/b] sont des elfes de sang qui résident dans [zone=3703] dirigé par [npc=18530]. Le groupe s\'est éloigné de [npc=19622] et a offert de leur aide au Naaru de Shattrath. Ils sont en désaccord avec [faction=932], et rivalisent avec eux pour le pouvoir de Shattrath et la faveur du Naaru. \n\nLa plupart des joueurs commenceront avec une réputation neutre auprès des Clairvoyants. [npc=18166] à Shattrath donnera aux joueurs une première quête pour devenir amical avec lAldor ou Les Clairvoyants. Ce choix est réversible si les joueurs ressentent le besoin. \nLes joueurs delfes de sang seront amicaux avec les Clairvoyants et hostiles avec l\'Aldor, alors que les joueurs draenei seront hostiles aux Clairvoyants et amicaux envers lAldor.\n\n[npc=19331] et [npc=20808] sont situés dans la banque des Clairvoyants, sur le bord sud de la terrasse de lumière. La Bibliothèque du Visiteur abrite [npc=20613] [small][/small] et [npc=21905] [small][/small], qui échangent des pièces d\'armure épique contre des pièces de set de[url=?Itemsets&filter=ta=12]Niveau 4[/url] et de [url=?Itemsets&filter=ta=13]Niveau 5[/url].\n\n[i]Note : Les gains de réputation avec les Clairvoyants correspondent à une perte de réputation de 10% plus élevée chez lAldor. La plupart des gains de réputation avec les Clairvoyants accorderont également 50% de la réputation avec [faction=935].[/i]\n\n[h3]Tradition [/h3]\n\nAprès avoir subi des assauts implacables de leurs ennemis, les gardes harassés de Sha\'tar et de lAldor se sont regroupés pour la prochaine attaque alors qu\'elle marchait sur l\'horizon. Cette fois, l\'attaque provenait des armées de [npc=22917]. Un grand régiment d\'elfes de sang avait été envoyé par l\'allié d\'Illidan, le prince Kael\'thas pour détruit la ville. Alors que le régiment d\'elfes de sang traversait le pont, les exarques et les vindicateurs de lAldor se sont alignés pour défendre la Terrasse de Lumière. Alors l\'inattendu arriva, les elfes de sang déposèrent leurs armes devant les défenseurs de la ville.\nLeur chef, un ainé de sang connu sous le nom de Voren\'thal, a exigé de parler au naaru [npc=18481]. À mesure que le naaru s\'approchait de lui, Voren\'thal s\'agenouilla et prononça les mots suivants : « Je vous ai vu dans une vision, naaru. Le seul espoir de survie de ma race est avec vous. Mes disciples et moi-même sommes là pour vous servir ».\nLa défection de Voren\'thal et de ses partisans a été la plus grande perte jamais subie par les forces de Kael\'thas. Beaucoup des plus forts et les plus brillants parmi les savants et les magistrats de Kael\'thas ont été influencés par l\'influence de Voren\'thal. Le naaru a accepté les déflecteurs qui sont devenus connus sous le nom de Clairvoyant.\n\n[h3]Réputation[/h3]\n\n[b]Jusqu\'à honoré[/b]\n\nLes joueurs qui cherchent à gagner les rangs de réputation supérieurs (Révéré, Exalté) peuvent vouloir sauver des quêtes non répétables jusqu\'à ce qu\'ils soient honorés.\n\nDonner 10 [span class=q1][item=29426][/span] à [npc=18531] dans la bibliothèque du Visiteur des Clairvoyants accordera une réputation de 250 points de réputation pour les Clairvoyants. Il existe également une quête répétable où donner une unique chevalière accorde 25 points de réputation. Ces chevalières tombent sur des membres Aile-de feu dans la partie nord-est de la forêt de Terrokar. \nEnviron 240 marques sont nécessaires pour passer d\'amical à honoré.\nEn outre, ces quêtes fournissent de la réputation de Sha\'tar ; 125 points de réputation pour 10 marques ou 12,5 points de réputation pour une unique chevalière.\n\n[b]Jusqu\'à exalté [/b]\n\nUne fois que vous atteignez le niveau 68, vous pouvez également donner 10 [span class=q1][item=30810][/span], cest le même principe que les chevalières mais ceux-ci tombent sur des elfes de sang Solfurie de haut rang. Si vous le souhaitez, vous pouvez transformer les chevalières de niveau supérieur avant une réputation honorée. Vous les trouverez dans [zone=3523], [zone=3520] et les instances du [url=?Search=tempête+donjon]donjon de la tempêtes[/url].\n\n[b]Tome des Arcanes[/b]\n\n[span class=q2][item=29739][/span] peut être donné à tout moment à [npc=18530] à l\'intérieur la Bibliothèque du Visiteur. Cela augmentera votre réputation avec les Clairvoyants de 350 par Tome des Arcane.\nEn plus des gains de réputation, vous recevrez une [span class=q1][item=29736][/span], qui est la condition pour acheter l\'enchantements d\'épaule à [npc=20808], qui réside dans la banque des Claivoyants.\n\n[h3]Passer à la réputation des Claivoyants[/h3]\n\nPour changer votre faction d\'Aldor vers Claivoyants et donc accéder à leurs recettes d\'artisanat (et annuler toutes les avancées de réputation que vous avez faites), trouvez [npc=18596], membre des Claivroyants dans la ville basse. Elle vous propose une quête répétable, [quest=10024], où pour huit [span class=q1][item=25744][/span] vous montez la réputation Claivoyant. Une fois que vous êtes neutre, vous ne pourrez plus recevoir cette quête.',NULL),(8,942,2,NULL,0,2,'L[b]Expédition Cénarienne[/b] a été envoyé par [faction=609], lors de la réouverture de la porte des ténèbres vers l\'Outreterre, pour explorer ce monde inconnu. Tout comme le cercle, il s\'agit d\'une coalition de forces entre les Elfes de la nuit et les Taurens. Depuis l\'ouverture de la porte, l\'expédition Cénarienne a rapidement gagné en taille et en autonomie, obtenant suffisamment de puissance pour être considérée comme une propre et unique faction. L\'expédition maintient sa base principale au refuge Cénarien dans [zone=3521], située immédiatement à louest de la péninsule des flammes infernales. Elle est aussi présente sur [zone=3483], dans [zone=3519], et dans [zone=3522]. \n\nLe Refuge est situé dans le marécage de Zangar afin détudier la faune riche située là-bas. Cependant, l\'expédition a révélé des retombées inquiétantes dans le marais. Les niveaux d\'eau dans de nombreuses régions du marécage diminuent, et certaines régions comme Morte-bourbe ont déjà beaucoup souffert de ce phénomène étrange. On sait que cette diminution des niveaux d\'eau peut être attribuée aux pompes qui ont été construites dans le marécage par les naga. Leur but est de créer un nouveau puits d\'éternité pour [npc=22917].\nCependant, l\'expédition ne peut pas se permettre une confrontation directe avec le naga si nombreux dans le marécage de Zangar et le [url=?Search=Glissecroc#c0z]Réservoir de Glissecroc [/url]. Elle a besoin de l\'aide daventurier qui veulent soutenir les druides dans leur dangereuse bataille contre les Nagas qui cherchent à perturber l\'équilibre naturel du marais. Naturellement, ceux assez héroïques pour combattre au réservoir de Glissecroc seront bien récompensés.\n\n[h3]Réputation[/h3]\n\n[b]De neutre à honoré[/b]\n\nTuez des Nagas chaque fois que vous le pouvez. Le mieux sera de parcourir les instances, la réputation monte plus rapidement.\nAlternativement, le joueur peut commencer à trouver des [item=24401] pour avoir une chance davoir des [item=24407], qui peuvent être transformé en 500 points de réputation. Il est suggéré que le joueur garde ses espèces non cataloguées jusqu\'à ce que son statut honoré soit atteint, car la quête ne peut pas être poursuivie après ce point, alors que les espèces non cataloguées peuvent être utilisées jusqu\'à Exalté.\n\nSi vous êtes un herboriste et que vous êtes intéressé par la réputation [faction=970], vous voudrez peut-être trouver les [url=?Npcs&filter=na=Seigneur+tourbe]Seigneurs-tourbes[/url] qui se trouve dans lEst, et le coin Sud-ouest du Marécage de Zangar. Leurs corps peuvent être «récoltés» par les herboristes et produisent souvent des végétaux non identifiées, alors que chaque monstre tué donne 15 points de réputation chez Sporeggar. \n\n[b]De honoré à révéré[/b]\n\nUne fois que le joueur est honoré, faire lenclos aux esclaves et [zone=3716] (à l\'exception de [npc=17770] et de certains géants), n\'accorderont plus de réputation. Vous devriez maintenant faire des quêtes de l\'Expédition Cénarienne dans la péninsule des flammes infernal, le marécage de Zangar, la forêt de Terokkar et les Tranchantes. Il est également temps de transformer toutes les espèces non cataloguées que vous avez trouvées. Faire cela devrait vous faire passer révérer.\n\nAlternativement, vous pouvez, en étant niveau 70, faire [zone=3715]. Chaque donjon donne un peu plus de 1500 points de réputation si vous tuez toutes les mobs.\nDans le Caveau de la vapeur, se trouve, aussi, une quête répétable, [quest=9764], qui commence par [item=24367]. Vous pourrez ensuite donner les [item=24368], qui tombe à la fois dans le caveau de la vapeur et lenclos aux esclaves, recevant 250 points de réputation pour les premières armes et 75 points de réputation par la suite. Cette quête est disponible jusqu\'à exalté.\n\nUne fois que vous avez le niveau 70 et que vous avez amélioré votre équipement, vous pouvez choisir d\'entrer dans lenclos des esclaves, le caveau de la vapeur et basse-tourbière en mode héroïque avec l\'achat de la [item=30623]. Ils accordent une réputation importante : les mobs ordinaires valent 15 points de réputation, 2 pour les non élites et 150 à 250 pour les boss. Cette méthode fonctionne jusqu\'à exalté.\n\n[b]De révéré à exalté [/b]\n\nContinuez avec la même stratégie que ci-dessus : terminez toutes les requêtes restantes, faites caveau de la vapeur et continuez avec la quête des [item=24368].\n\nIl est également possible de faire lenclos des esclaves, Basse-tourbière et caveau de la vapeur en mode héroïque. La réputation acquise n\'est pas beaucoup plus intéressante que le caveau de la vapeur en mode normal, alors que l\'investissement dans le temps pour les donjons héroïques est beaucoup plus élevé, le butin est mieux et vous recevrez [item=29434] sur les boss qui peuvent être utilisés pour acheter des équipements épiques de haute qualité.',NULL),(8,941,2,NULL,0,2,'Les [b]Mag\'har[/b] sont la faction d\'orcs à peau brune qui sont restées en Outreterre et se sont séparés des autres clans orcs restants qui ont été victimes de [npc=17257] et qui sont maintenant dirigés par le puissant [npc=16808]. Les Mag\'har sont présent dans la forteresse de Garadar dans le magnifique pays de [zone=3518], une fois bien installés, la majorité des orcs sont retournés dans [zone=3519] et [zone=3522].\n\nLes Maghar n\'ont jamais été corrompus par Mannoroth ou Magtheridon. Contrairement à dautres anciens clans qui vivent dans les ruines de leurs ancêtres, les Mag\'har sont composés de membres de différents clans d\'orc qui ont échappé à la corruption. Le chef actuel des Mag\'har, la vénérable [npc=18141], est une orc ancienne et sage, mais elle est tombée récemment extrêmement malade. [npc=18063], fils du puissant Grom hurlenfer, sert de chef militaire aux Mag\'har, aidé par [npc=18106], fils du vénérable chef du clan Orbite-Sanglante, Kilrogg Deadeye. En outre, il existe un orc dans un camp de Mag\'har à l\'ouest connu sous le nom [npc=18229].\n\nIl n\'est pas clair comment le Mag\'har a réussi à conserver sa peau marron d\'origine. La peau orque devient verte lorsqu\'elle est exposée à la magie du sorcier, indépendamment des croyances ou des pratiques de l\'individu ; Garrosh et Jorin auraient certainement été exposés, compte tenu de la position hiérarchique de leurs pères.\n\nLes joueurs de la Horde commencent inamical avec le Mag\'har. Les joueurs de l\'Alliance seront toujours traités comme hostiles. La contrepartie de l\'Alliance à cette faction est la faction des : [faction=978].\n\n[h3]Quête[/h3]\n\nLes quêtes pour les Mag\'har commencent dans [zone=3483] avec [quest=9400] de [faction=947]. Cette quête vous mènera à un petit avant-poste Mag\'har au nord de la Citadelle des flammes infernales. Une fois à Nagrand, les joueurs trouveront la principale ville de Mag\'har, Garadar. La ville détient la plupart des quêtes restantes qui récompenseront la réputation de Mag\'har.\n\n[i]Note : Vous DEVEZ compléter la suite de quête de \"lassassin\" jusqu\'à la quête [quest=9410] (où vous devenez neutre) afin que vous puissiez parler à la plupart des gens de Garadar.[/i]\n\n[h3]Réputation[/h3]\n\nLa réputation peut être obtenue en tuant des [url=?npcs&filter=na=kil%27sorrau;ra=-1;rh=-1]Membres de culte Kil\'sorrau[/url], des [url=?Npcs&filter=na=Bourbesang;ra=-1;rh=-1;cr=6;crs=3518;crv=0]Bourbesang[/url], des [url=?Npcs&filter=na=cogneguerre+-marker]Cogneguerre[/url] et des [url=?Npcs&filter=Na=rochepoing;minle= 64;ra=-1;rh=1]Rochepoing[/url] à Nagrand. Les joueurs peuvent également transformer 10x[item=25433], qui tombent de ces ogres.\n\nLes joueurs qui recherchent la réputation : [faction=933] peuvent vouloir garder leurs perles, car la réputation Mag\'har est généralement plus facile à obtenir. \nLes joueurs qui recherchent la réputation :[faction=932] peuvent préférer tuer les membres du culte à la forteresse de Kil\'Sorrau, car ils donnent aussi des [item=29425] pour la réputation Aldor.\n\n[i]Remarque : Ces monstres et quêtes n\'ont pas de limite, ils accordent une réputation jusquà exalté![/i]',NULL),(8,946,2,NULL,0,2,'Le [b]Bastion de lHonneur[/b], refuge des explorateurs humains, élu, draenei et nains, est la première grande ville que les explorateurs de l\'Alliance rencontreront en traversant la porte des ténèbres. Les vestiges des fils de Lothar, anciens combattants de l\'Alliance qui sont venus à Draenor, se sont tenus fermement dans cet avant-poste des flammes infernales. Ils sont maintenant rejoints par les armées de Hurlevent et Forgefer.\n\n[h3]Réputation[/h3]\n\nLa réputation du Bastion de l\'Honneur est gagnée par divers moyens dans la péninsule des flammes infernales. Les PNJs, dans et autour, de la citadelle donnent en récompensés de quêtes de l\'honneur et de la réputation. En raison du manque de représentants dans d\'autres endroits dOutreterre il y a un grand écart entre Honoré et Exalté, au cours duquel il est possible que vous ne puissiez pas obtenir assez de réputation au bastion de lhonneur une fois que vous partez de la péninsule.\n\n[b]Jusquà Honoré[/b]\n\nTuer des Pnjs dans [zone=3562] et [zone=3713] attribueront de la réputation. Une option est de faire les donjons jusqu\'à ce que la réputation arrive à honoré avant de faire des quêtes du Bastion de l\'honneur, car les quêtes continuent à donner de la réputation jusqu\'à Exalté.\n\nVous voudrez peut-être tuer les orcs à lextérieur du bastion qui donnent une réputation si vous êtes Neutre. La réputation donnée sarrête une fois que vous êtes amicales.\n[ul]\n[li][npc=19415][/li]\n[li][npc=16878][/li]\n[li][npc=16870][/li]\n[li][npc=16867][/li]\n[li][npc=19414][/li]\n[li][npc=19413][/li]\n[li][npc=19411][/li]\n[li][npc=19422][/li]\n[/ul]\n\n[b]PvP[/b]\n\nLes joueurs qui apprécient le PvP peuvent gagner de l\'honneur et de la réputation avec la quête [quest=10106]. Cette quête accorde 70 points d\'honneur et 150 points de réputation au Bastion de lHonneur, mais ne peut être complétée qu\'une fois par jour et compte pour votre limite de 25 quêtes journalières. L\'achèvement de cette quête fournit également trois [span class=q1][item=24579][/span], qui sont utilisés comme monnaie pour divers types d\'articles lorsqu\'ils sont échangés chez [npc=17657] et [npc=18266] au Bastion de lHonneur ainsi que [npc=18581] aux marécages de Zangar.\n\n[b]Jusquà Exalté[/b]\n\nÀ partir de là, il n\'y a que deux façons d\'atteindre Révéré et Exalté :\n[ul]\n[li][zone=3714], cette instance nécessite le niveau 68 et [span class=q1][item=28395][/span] (Un seul membre du groupe a besoin de la clé). Linstance des salles brisées abrite des PNJs qui donnent de la réputation jusquà Exalté.[/li]\n[li]Après avoir obtenu le statut dhonoré, vous pouvez acheter [span class=q1][item=30622][/span] qui accorde l\'accès au mode héroïque des instances de la citadelle des flammes infernales. Faire les donjons en mode Héroique donneront plus de réputation que les salles brisées en mode normale et continueront à donner de la réputation jusquà Exalté.[/li]\n[/ul]\n\n[i]Astuce : Vous pouvez utiliser ces marques pour acheter [span class=q1][item=24520][/span] à l\'adjudant Tracy Proudwell et augmenter le montant gagné de réputation (et dexpérience) acquise lors de l\'exécution de ces instances.[/i]',NULL),(8,967,2,NULL,0,2,'[b]L\'Oeil Pourpre[/b] est une secte secrète fondée par le Kirin Tor de Dalaran pour espionner le gardien de Tirisfal, [npc=15608], dans la tour de [zone=2562]. Bien que Medivh soit mort, l\'il pourpre reste dans Karazhan, défendant le mal qui semble lenvahir en l\'absence de son maître.\n\nOn ignore si l\'apprenti de Medivh, [npc=18166], était membre de lOeil Pourpre, ou s\'il connaissait leurs activités à l\'époque.\n\n[h3]Réputation[/h3]\n\nLa réputation de lil pourpre est obtenue en tuant des mobs à l\'intérieur de Karazhan et en complétant les quêtes liées à Karazhan. La réputation grâce aux mobs de Karazhan peut être acquise à partir d\'une position neutre jusquà une réputation exalté. Chaque mob apporte une réputation d\'environ 15 points, les boss accordent davantage de réputation.\n\n[npc=18253] propose une chaîne de quête assez longue commençant par [quest=9824] et [quest=9825]. Cette suite de quête se termine par [quest=9644] et récompense les joueurs avec [span class=q1][item=24490][/span]. L\'achèvement complet de cette suite de quête récompense le joueur avec 10 270 point de réputation d\'environ.\n\n[h3]Récompenses de la réputation[/h3]\n\n[npc=18253] offrira aux joueurs des bagues en récompenses pour chaque niveau de réputation sous forme de quêtes. La première de ces quêtes est disponible dès la réputation neutre. Vous recevrez une version nouvelle et améliorée de la bague que vous avez choisi chaque fois que vous entrez dans un nouveau niveau de réputation. Les anneaux sont triés dans les 4 catégories suivantes :\n[ul]\n[li][quest=10731] : [item=29280], [item=29281], [item=29282] et [item=29283][/li]\n[li][quest=10729] : [item=29284], [item=29285], [item=29286] et [item=29287][/li]\n[li][quest=10732] : [item=29276], [item=29277], [item=29278] et [item=29279][/li]\n[li][quest=10730] : [item=29288], [item=29289], [item=29291] et [item=29290][/li]\n[/ul]\n\n[npc=16388], un forgeron situé à l\'intérieur de Karazhan juste après [npc=15550], offre aux joueurs ayant une réputation assez élevée la possibilité d\'acheter des plans de forge épique. Les joueurs honorés ou au-dessus pourront également réparer des armures et des armes chez ce fournisseur.\n\n[npc=18255], qui se trouve juste à l\'extérieur des portes principales de Karazhan, vendra une recette de joaillerie épique et un enchantement d\'épaule aux joueurs qui ont une haute réputation avec lOeil Pourpre.',NULL),(8,970,2,NULL,0,2,'Les[b]Sporeggar[/b] sont une race de champignons essentiellement pacifique originaire d\'Outreterre. Ils vivent dans une ville située dans les tourbières occidentales de [zone=3521].\n\n[h3]Réputation [/h3]\n\nLes joueurs de l\'Alliance et de la Horde commencent amicalement avec Sporeggar. Il existe de nombreuses façons d\'augmenter votre réputation au début : \n[ul]\n[li]Apporter 10 [span class=q1][item=24290][/ span] à [npc=17923] pour compléter [quest=9739][/li]\n[li]Apporter 6 [span class=q1][item=24291][/span] à Fahssn pour compléter [quest=9743][/li]\n[i]Ces deux quêtes ne seront disponibles que si vous avez une réputation au minimum amical[/i]\n[li]Tuer [url=?Search=seigneurs +tourbes+-hungry #z0z]Seigneurs tourbes[/url] [i](jusqu\'à honoré)[/i][/li]\n[li]Tuer [npc=18137] et [npc=18136] [i](jusqu\'à révéré)[/i][/li]\n[li]Apporter 10 [span class=q1][item=24245][/span] à [npc=17924] dans Sporeggar[i] (jusquà amical)[/i][/li]\n[/ul]\n\nAprès avoir une réputation [b]amicale[/b], de nouvelles quêtes répétitives s\'ouvrent en même temps que les quêtes de Fahssn, notamment :\n[ul]\n[li]Tuer 12 [npc=18088] et [npc=18089] pour [npc=17856] pour compléter [quest=9726][/li]\n[li]Apporter 10 [span class=q1][item=24449][/span] à [npc=17925] pour compléter [quest=9806][/li]\n[li] S\'aventurer dans [zone=3716] pour rassembler 5 [span class=q1][item=24246][/span] pour terminer [quest=9715][/li]\n[/ul]\nCes 3 quêtes sont répétables et seront disponibles jusquà la réputation exalté.\nLes joueurs qui sont exaltés avec Sporeggar devraient parler à [npc=17877] pour une dernière quête.',NULL),(8,978,2,NULL,0,2,'Les Kurenaï, pour « racheté », ont échappé à lesclavage en Outreterre et ont fait leur maison à Telaar dans le sud de [zone=3518]. C\'est là qu\'ils cherchent à redécouvrir leur destinée. Ils conservent également une petite présence en [zone=3521]. Leur intendant, [npc=20240], est situé à l\'extérieur de l\'auberge à Telaar, en dessous du point de vol.\n\nLes joueurs de l\'Alliance commencent à faire preuve d\'hostilité avec les Kurenai. Les joueurs de la Horde seront toujours traités comme hostiles. La contrepartie de la Horde à cette faction est [faction=941].\n\n[i]Kurenai est le japonais pour « cramoisi ».[/i]\n\n[h3]Réputation[/h3]\n\nLa réputation peut être obtenue en tuant des [url=?Npcs&filter=na=kil%27sorrau;ra=-1;rh=-1]Membres de culte Kil\'sorrau[/url], des [url=?Npcs&filter=na=Bourbesang;ra=-1;rh=-1;cr=6;crs=3518;crv=0]Bourbesang[/url], des [url=?Npcs&filter=na=cogneguerre+-marker]Cogneguerre[/url] et des [url=?Npcs&filter=Na=rochepoing;minle= 64;ra=-1;rh=1]Rochepoing[/url] à Nagrand. Les joueurs peuvent également transformer 10x [item=25433], qui tombent de ces ogres.\n\nLes joueurs qui cherchent la réputation de la faction [faction=933] peuvent vouloir garder leurs perles, car la réputation de Kurenai est généralement plus facile à obtenir.\n\nLes joueurs qui cherchent la réputation de la faction [faction=932] peuvent préférer tuer les membres du culte à la forteresse de Kil\'Sorrau, alors qu\'ils donnent des [item=29425] pour la réputation de lAldor.\n\n[i]Remarque : Ces monstres et quêtes n\'ont pas de limite, ils accordent de la réputation jusquà exalté.[/i]',NULL),(8,989,2,NULL,0,2,'Les [b]Gardiens du Temps[/b] sont des dragons de bronze sélectionnés par Nozdormu pour surveiller les grottes du temps. Ils sont dirigés par [npc=19932] et [npc=19933], qui remplacent également Nozdormu en son absence.\n\n[h3]Réputation[/h3]\n\nActuellement, la seule façon d\'obtenir la faveur des dragons de bronze est de faire les instances : [zone=2367] et [zone=2366]. Lintendant des Gardiens du Temps, [npc=21643], se situe au quartier-intendant dans les grottes du temps. Les Gardiens vous demanderont d\'être au minimum niveau 66 et de compléter la courte quête [quest=10277] avant d\'autoriser le passage dans Les contreforts dHautebande dantan pour accomplir la destinée du Chef de la Horde, [npc=17876].',NULL),(8,990,2,NULL,0,2,'La [b]Balance des sables[/b] est un sous-groupe secret du vol des Dragons de bronze, dirigé par [npc=19935], premier partenaire de [npc=15185]. Leur chef, Nozdormu, a envoyé ces factions gardiennes à [zone=3606] où ils gardent l\'Arbre Monde d\'une autre attaque par les démons, contribuent à restaurer le temps et à préserver l\'avenir du monde.\n\n[h3]Réputation[/h3]\n\nTuer les boss et monstres du Fléau font monter la réputation. [npc=17968], le boss final, récompense de 1 500 points de réputation tandis que les quatre autres boss donnent 375 points de réputations. La réputation général des montres du Fléau donnent 12 points de réputation, tandis que [npc=17907] donnent 60 points de réputation. En produisant une moyenne de 7 800 points de réputations par raid, 6 raids sont nécessaires pour atteindre la réputation exaltée.\n\nActuellement, la réputation permet davoir lune des meilleurs [span class=q4][url=?Items=4.-2&filter=na=bague+éternel]Bagues[/url][/span] pour les raids. Afin de recevoir ces anneaux, vous devez compléter la quête précédemment requise, [quest=10445]. Chaque nouveau niveau de réputation accorde une bague améliorée.',NULL),(8,1012,2,NULL,0,2,'Les [b]Ligemorts Cendrelangues[/b] sont l\'élite de la tribu Kurenaï connue sous le nom de Cendrelangue. La tribu Cendrelangue est dirigée par la sage aînée [npc=21700]. Les Ligemorts sont [i]officiellement[/i] alignés avec [npc=22917] [small][/small]. Les Ligemorts sont les lieutenants les plus dignes d\'Akama et sont au courant des motivations mystérieuses de leur chef.\n\nPour découvrir les Ligemorts Centrelangues en tant que faction, le joueur doit commencer et compléter la majorité de la suite de quête qui commence par [quest=10568] ou [quest=10683]. Finalement, vous parlerez avec Akama, après quoi vous deviendrez neutre avec les Ligemorts Cendrelangues.',NULL),(8,947,2,NULL,0,2,'[b]Thrallmar[/b], expédition envoyée par le Portail des Ténèbres par Thrall, a construit un bastion dans la péninsule des flammes infernales qui sert de base d\'opérations pour une grande partie des activités de la Horde en Outreterre.\n\n[h3]Réputation[/h3]\n\nLa réputation de Thrallmar jusqu\'à l\'honorée est relativement facile à gagner. Même les quêtes les plus faciles (celles qui vous emmènent d\'un fournisseur de quête à la prochaine, par exemple) peuvent produire 75 points de réputation, alors que ceux qui nécessitent plus defforts pour compléter ont généralement 250 points de réputation ou plus. Certaines quêtes de groupe impliquant de tuer un élite peuvent donner jusqu\'à 1 000 points de réputation.\n\nSi vous faites la majeure partie des quêtes de Thrallmar au lieu de passer rapidement à la prochaine zone, vous pourriez vous attendre à être honoré après 1 ou 2 niveaux de jeu. En raison du manque de représentants dans d\'autres endroits dOutreterre il y a un grand écart entre Honoré et Exalté, au cours duquel il est possible que vous ne puissiez pas obtenir assez de réputation à Thrallmar une fois que vous partez de la péninsule. Cest seulement au niveau 68 que vous pouvez commencer à regagner des points dans le donjon [zone=3714].\n\n[b]Jusquà Honoré[/b]\n\nTuer des Pnjs dans [zone=3562] et [zone=3713] attribueront de la réputation. Une option est de faire les donjons jusqu\'à ce que la réputation arrive à honoré avant de faire des quêtes de Thrallmar, car les quêtes continuent à donner de la réputation jusqu\'à Exalté.\n\nVous voudrez peut-être tuer les orcs à lextérieur du bastion qui donnent une réputation si vous êtes Neutre. La réputation donnée sarrête une fois que vous êtes amicales.\n[ul]\n[li][npc=19415][/li]\n[li][npc=16878][/li]\n[li][npc=16870][/li]\n[li][npc=16867][/li]\n[li][npc=19414][/li]\n[li][npc=19413][/li]\n[li][npc=19411][/li]\n[li][npc=19422][/li]\n[/ul]\n\n[b]PvP[/b]\n\nLes joueurs qui apprécient le PvP peuvent gagner de l\'honneur et de la réputation avec la quête [quest=10110]. Cette quête accorde 70 points d\'honneur et 150 points de réputation à Thrallmar, mais ne peut être complétée qu\'une fois par jour et compte pour votre limite de 25 quêtes journalières. L\'achèvement de cette quête fournit également trois [span class=q1][item=24581][/span], qui sont utilisés comme monnaie pour divers types d\'articles lorsqu\'ils sont échangés chez [npc=18267] et [npc=18564] à Thrallmar et près de Zabrajin dans [zone=3521].\n\n[b]Jusquà Exalté[/b]\n\nÀ partir de là, il n\'y a que deux façons d\'atteindre Révéré et Exalté :\n[ul]\n[li][zone=3714], cette instance nécessite le niveau 68 et [span class=q1][item=28395][/span] (Un seul membre du groupe a besoin de la clé). Linstance des salles brisées abrite des PNJs qui donnent de la réputation jusquà Exalté.[/li]\n[li]Après avoir obtenu le statut dhonoré, vous pouvez acheter [span class=q1][item=30637][/span] qui accorde l\'accès au mode héroïque des instances de la citadelle des flammes infernales. Faire les donjons en mode Héroique donneront plus de réputation que les salles brisées en mode normale et continueront à donner de la réputation jusquà Exalté.[/li]\n[/ul]\n\n[i]Astuce : Vous pouvez utiliser ces marques pour acheter [span class=q1][item=24522][/span] au Crieur-de-guerre Coquard et augmenter le montant gagné de réputation (et dexpérience) acquise lors de l\'exécution de ces instances.[/i]',NULL),(8,1011,2,NULL,0,2,'[b]Ville Basse[/b] de [zone=3703] est l\'endroit où les réfugiés se rassemblent et saident par leurs propres moyens. Lorsque vous aidez l\'une des races qui ont fui la guerre, la réputation se débrouille rapidement. Leur intendant, [npc=21655], est situé sur le marché dans la ville basse.\n\nLa ville basse de Shattrath contient de nombreux artisans qui possèdent de vastes connaissances :\n[ul]\n[li][npc=19187], [small]< Maître des travailleurs du cuirs >[/ small].[/li]\n[li][npc=19180], [small]< Maître des dépeceurs >[/small].[/li]\n[li][npc=19052], [small]< Maître des alchimistes >[/small]. Il donne la quête [quest=10902] (pour une spécialisation). Un laboratoire dalchimiste se trouve également à son côté.[/li]\n[li]Trois tailleurs qui vous permettent de se spécialiser et d\'acheter de nouvelles recettes de couture épiques pour des ensembles d\'armures et des sacs spéciaux :\n[ul][li][npc=22212], [small]< Spécialiste de couture de tisse-ombre >[/small] vend des recettes pour [itemset=553][/li]\n[li][npc=22213], [small]< Spécialiste de couture de feu-sorcier >[/small] vend des recettes pour [itemset=552].[/li]\n[li][npc=22208], [small]< Spécialiste de couture détoffe lunaire > [/small] vend des recettes pour [itemset=554].[/li][/ul]\n[/ul]\n\nLes maîtres de guerre, Alliance et Horde, des quatre [zones=6] peuvent également être trouvés ici, ainsi que la Tavernes de la Fin du Monde.\n\n[h3]Réputation[/h3]\n\n[b]Jusqu\'à honoré [/b]\n[ul]\n[li]Faire [zone=3790] en [i]mode normal[/i], vous récompense denvirons 750 points de réputation.[/li]\n[li]Faire [zone=3791] en [i]mode normal[/i], vous récompense denvirons 1 250 points de réputation.[/li]\n[li]Faire [zone=3789] en [i]mode normal[/i], vous récompense denvirons 2 000 points de réputation.[/li]\n[li]Fournir 30 x [item=25719] à [npc=22429], vous récompense de 250 points de réputations par quête.[/li]\n[/ul]\n[i]Note : Les joueurs qui visent une faction supérieure à Honorée devraient attendre jusqu\'à dêtre honoré avant de compléter les quêtes de la Ville Basse.[/i]\n\n[b]De honoré à exalté[/b]\n[ul]\n[li]Faire de Labyrinthe des ombres en [i]mode normal[/i], vous récompense de 2 000 points de réputation.[/li]\n[li]Terminer toutes les [url=?quests&filter=cr=1;crs=1011;crv=0]quête de la Ville-Basse[/url].[/li]\n[/ul]\n[b]De révéré à exalté[/b]\n[ul]\n[li]Faire les Cryptages Auchenai en [i]mode héroïque[/i], vous récompense denvirons 750 points de réputation.[/li]\n[li]Faire les salles de Sethekk en [i]mode héroïque[/i], vous récompense denvirons 1 250 points de réputation.[/li]\n[li]Faire le Labyrinthe des ombres en [i]mode normal[/i] ou en [i]mode héroïque[/i], vous récompense denvirons 2 000 points de réputation.[/li]\n[/ul]\n\n[h3]Anecdotes[/h3]\n\n[npc=19227], un vendeur dans la ville basse, vend des amulettes qui sont très ... intéressantes. Il vend des articles comme [item=27940], qui vous permettent de revenir à la vie lorsque vous retournez à l\'endroit où vous êtes mort. [i]Buyer se méfiez-vous![/i]\n\nEn tant quexalté, vous pouvez acheter un [item=31778]. Curieusement, aucun des habitants de la Ville Basse na été vu avec un tel objet. Peut-être qu\'ils ne peuvent pas se le permettre',NULL),(8,1015,2,NULL,0,2,'L[b]Aile-du-Néant[/b] est une faction de dragons situés en Outreterre. La couvée inhabituelle a été engendrée par les ufs du vol de dragon noir dAile-de-Mort et infusée d\'énergies brutes. Maintenant, ils cherchent à trouver leur identité au-delà de l\'ombre du patrimoine destructeur de leur père.\n\n[h3]Réputation[/h3]\n\nLes joueurs, au commencement, sont haïe à la faction Aile-du-Néant et doivent être exaltés pour recevoir des [span class=q4][url=?Items=15.-7&filter=na=Aile-du-Néant+Drake]Drakes Aile-du-Néant[/url][/spanclass]. La suite de quête de la réputation est une suite qui se fait en solitaire impliquant des quêtes journalières, une quête de groupes (5 joueurs) pour passer Neutre et les quêtes journalières de groupe (3 joueurs) après être passer Révéré.\nUne monture volante est requise pour cette réputation et 300 compétences de monte sont nécessaires pour passer neutre.\n\n[b]De Haïe à Neutre[/b]\n\nLes joueurs de niveau 70 commenceront leur voyage pour une réputation exaltée en choisissant la suite de quête offerte par [npc=22113], un elfe du sang errant la surface des champs dAile-du-Néant, dans le coin sud-est de [zone=3520]. La suite de quête commence par [quest=10804]. L\'achèvement de cette suite fournira une réputation instantanée neutre et le choix de l\'un de [span class=q3][url=?Items&filter=qu=18;cr=1;crv=0;na=Aile%20néant;qu=3]ces 5 items[/url][/span].\n\n[h3]Après Neutre [/h3]\n\nAprès avoir terminé la suite de quête, Mordenai sassurera qui vous ayez acquis 300 compétences [spell=34091] et que vous ayez une réputation neutre auprsè de lAile-de-Néant.\nCela vous accordera un déguisement dOrc Gueule-de-Dragon lorsque vous entrez dans la zone Aile-du-Néant et vous permettra de communiquer et de travailler pour les Gueules-de-Dragon stationné là-bas.\n\nMordenai vous enverra d\'abord à [npc=23139] avec un ensemble de faux papiers. L\'achèvement de cette quête débloque le début des quêtes Gueule-de-Dragon sur lesquelles vous travaillerez pour augmenter votre réputation Aile-du-Néant.\n\nLa plupart de ces quêtes seront journalières (ajoutée à la 2.1). Les quêtes journalières diffèrent des quêtes régulières car elles sont infiniment repérables, mais vous ne pouvez compléter chaque quête journalière qu\'une fois par jour et se limiter à 25 quêtes journalières par jour.\n[i]Remarque : De nouvelles quêtes seront débloquées après chaque niveau de réputation, et toutes les quêtes journalières des niveaux précédents seront toujours disponibles.[/i]\n\n[b][toggler id=Neutralcaché]Neutre[/toggler][/b]\n\n[div id=Neutralcaché] \nAprès avoir donné la [item=32469] à [npc=23139] pour compléter [quest=11013], votre première suite de quêtes sera disponible pour accéder au prochain niveau de réputation avec Aile-du-Néant.\n\nMor\'ghor vous indiquera daller voir le maître d\'uvre afin de commencer votre travail, et [npc=23141] se révélera comme un allié déguisé et vous proposera dautres quêtes.\nL\'une d\'entre elles est [quest=11049]. Les joueurs pourront trouver, avec un peu de chance (1% de loot), l[item=32506] sur presque toutes les créatures de lescarpement dAile-du-Néant et sur un [item=185881] ou un [item=185877].\nYarzill voudra aussi une trouvaille rare, l[item=185915], trouvée n\'importe où sur le rebord dAile-du-Néant et dans la forteresse Gueule-de-Dragon, coin sud-est de la vallée de dOmbrelune. Cette quête n\'est pas étiquetée comme journalière et peut donc être effectuée autant de fois que vous voulez, du moment que vous pouvez trouver des ufs. Cette quête nest pas comprise dans votre limite de quête journalière.\n\nAutres quêtes disponibles dès le début:\n[ul]\n[li][i][small]Journalière[/small][/i] - [quest=11018], [quest=11016], [quest=11017] Nest disponible que pour les joueurs qui possèdent la profession adaptée pour rassembler chaque élément.[/li]\n[li][i][small]Journalière[/small][/i] - [quest=11015] - Une quête de collecte simple ouverte à tous les joueurs indépendamment de leur profession.[/li] \n[li][i][small]Journalière[/small][/i] - [quest=11020] - Yarzill vous demandera de collecter des [item=32502]s et de les utiliser afin dempoisonner les péons qui travaillent pour rassembler des ressources pour Gueule-de-Dragon.[/li]\n[li][i][small]Journalière[/small][/i] - [quest=11035] - Vous devrez voler vers le coin nord-est de lescarpement dAile-du-Néant et vous positionner sur une des roches flottantes pour intercepter le [npc= 23188] et récupérer 10 x [item=32509].[/li]\n[/ul]\n[/div]\n[b][toggler id=Friendlyhidden]Amical[/toggler][/b]\n\n[div id=Friendlyhidden]\nMor\'ghor vous donnera un [item=32694] pour circuler avec votre nouveau rang parmi les Gueules-de-Dragon.\n[ul]\n[li][quest=11083] - [npc=23166] vous enverra tuer des bourbesangs qui sont stationné profondément dans les mines.[/li]\n[li][quest=11081] - Après avoir trouvé les [item=32726] dans un [item=32724], vous révélerez ce qui se passe réellement avec les bourbesangs dans la mine.[/li]\n[li][quest=11054] - [npc=23291] vous donnera vos propres [item=32680] pour garder les pétons Gueules-de-Dragon en ligne et travailler avec efficacité[/li]\n[li][i][small]Journalière[/small][/i] - [quest=11076] - La [npc=23149] vous demandera de vous aventurer dans les mines Ailes-du-Néant et de récupérer la cargaison contenue dans les chars de la mine qui est jetée au hasard dans l\'intérieur de la mine.[/li]\n[li][i][small]Journalière[/small][/i] - [quest=11077] - L\'un des [npc=23376] vous informera que des créatures plus profondes dans la mine interrompent la production et vous demandent de réduire leur nombre.[/li]\n[li][i][small]Journalière[/small][/i] - [quest=11055] - Cette quête humoristique commence chez le [npc=23291] après que vous lui apportiez le matériel requis. Vous pourrez survoler lescarpement Aile-du-Néant et lancer le Booterang à n\'importe quel [npc=23311] qui sy trouve autour des cris-taux.[/li]\n[/ul]\n[/div]\n[b][toggler id=Honorécaché]Honoré[/toggler][/b]\n\n[div id=Honorécaché]\nMor\'ghor vous donnera votre nouveau [item=32695], qui est maintenant utilisable n\'importe où, tant que vous êtes à l\'extérieur.\n[ul]\n[li][quest=11063] - Cette quête en six parties est une course aérienne contre les autres maîtres de vol Gueule-de-Dragon. Ils tenteront tous de vous renverser, vous et votre monture, avec des attaques aériennes habilement placées, vous devez rester visible et sur votre monture jusqu\'à leur atterrissage, si vous échouez, vous devez redémarrer la quête. Après avoir vaincu le dernier des six coureurs, vous recevrez un [item=32863], qui fonctionne exactement comme une [item=25653]. Les effets des deux bijoux ne sadditionnent pas.[/li]\n[li][quest=11089] Le [npc=23427] demandera un ensemble de matériaux pour créer un dispositif spécial pour détruire son frère et entraver les avancées de la légion dans l\'ouest de [zone=3518].[/li]\n[li][i][small]Journalière[/small][/i] - [quest=11086] - Mor\'ghor Vous enverra au Portal de Nagrand pour tuer 20 [url=?npcs=7&filter=na=ombremort] Agents Ombremort[/url]. Attention aux seigneurs, ils patrouillent dans la région et peuvent vous tuer dcoup de poing.[/li]\n[/ul]\n[/div]\n[b][toggler id=Révéréhidden]Révéré[/toggler][/b]\n\n[div id=Révéréhidden]\nMor\'ghor vous donnera votre nouveau [item=32864], le plus haut bijou.\n[ul]\n[li][url=?quests&filter=na=tuez%20les%20tous;minle=70;maxle=70] Tuez-les tous ![/url] - Mor\'ghor vous ordonnera de commencer l\'attaque la base d\'opérations de votre faction dans la vallée de Sombrelune. De toute évidence, vous n\'allez pas autoriser les Gueules-de-Dragon à attaquer vos alliés, alors vous informerez au leader approprié et débloquerez votre dernière quête journalière pour les Gueules-de-Dragon.[/li]\n[li][i][small]Journalière[/small][/i] [url=?quests&filter=na=le%20plus%20mortel%20des%20pièges]Le plus mortel des pièges[/url] - Les forces Gueules-de-Dragon vont attaquer la base des opérations. Apportez des alliés, car il s\'agit d\'une grande bataille.[/li]\n[/ul]\n[/div]\n[b][toggler id=Exaltécaché]Exalté[/toggler][/b]\n\n[div id=Exaltécaché]\nAprès de nombreux jours de travail, finalement le dénouement de la suite des quêtes Aile-du-Néant / Gueule-de-Dragon, vous dirigera à Mor\'ghor une dernière fois, qui vous informera que vous serez promu par [npc=22917] lui-même.\nSans gâcher les événements qui s\'ensuivent, vous vous retrouverez à Shattrath avec une sélection de montures épiques Aile-du-Néant. Vous pouvez en choisir un gratuitement, et si vous décidez d\'une couleur différente plus tard, vous pouvez acheter un autre drake chez [npc=23489] dans le camp de Gueule-de-Dragon pour 200 or.\n[/div]',NULL),(8,1031,2,NULL,0,2,'Les [b]Gardes-ciel sha\'tari[/b] sont les gardiens aériens de [zone=3703], défendant la capitale des assaillants dans les collines ainsi que la lutte contre les Arakkoas de Terokk dans les sommets de Skettis. [faction=935] dirigent les gardes-ciel shatari.\nIls ont deux avant-postes, l\'un au nord des montages de Skettis et un près d[faction=1038]. Les joueurs commencent avec une réputation neutre chez les Gardes-ciel sha\'tari.\n\n[h3]Réputation[/h3]\n\n[b]Quêtes journalières[/b]\n[ul]\n[li][quest=11008] - [npc=23048] vous accordera un paquet d\'explosifs pour détruire les oeufs qui reposent au sommet des structures de Skettis. [/li]\n[li][quest=11085] - Le [npc=23383] peut être trouvé au sommet de certaines structures, les joueurs l\'escorteront pour la réputation, l\'or et un choix entre deux potions : [item=28100] ou [item=28101].[/li]\n[li][quest=11065] - [npc=23335] vous informera que les bombardements, de lavant-poste de la garde-ciel, ont coûté la vie de leurs montures et vous demandent de rassembler des Raies de léther pour compléter leurs forces aériennes.[/li]\n[li][quest=11010] - [npc=23120] vous demande de détruire les munitions pour les canons de la Légion afin que les gardes-ciel puissent continuer leur travail.[/li]\n[li][quest=11004] - Après avoir recueilli 6 [item=32388], [npc=23042] fera une potion qui permettra de voir l\'arakkoa le plus puissant, tel que [npc=23066].[i][small] Note : cette quête n\'est pas une quête journalière, mais peut être répété autant de fois que nécessaire. [/small][/i][/li]\n[/ul]\n\n[b]Créatures[/b]\n\n[ul]\n[li][npc=21804] - 5 points de réputation, jusqu\'à la fin de Révéré[/li]\n[li][url=?npcs&filter=na=skettis+-kaliri+-assassin;minle=70] Tous les Arakkoa de Skettis[/url] - 10 points de réputation.[/li]\n[li][npc=23029] - 30 points de réputation.[/li]\n[/ul]',NULL),(NULL,NULL,0,'new',0,2,'Any user can write a guide and then share it with the community. Before a guide will be available to the public, it will be put in a queue where it can be approved or rejected by the staff. We suggest that you make sure your guide is complete before you put it through this process. A complete guide will generally be thorough, 100% accurate for World of Warcraft\'s current build, and include details such as images.\n\n[h3]Tips For Creating Quality Guides[/h3]\n\n[ul][li][b]Use [url=?help=markup-guide]Aowow\'s BBCode[/url].[/b][/li]\n[li][b]Choose the correct category.[/b] Guides placed in the wrong category risk being rejected. Don\'t see your category? Email [feedback]![/li]\n[li][b]Always submit only complete guides.[/b] You can save in-progress ones indefinitely so you won\'t risk losing them.[/li]\n[li][b]Make sure it\'s on a unique topic with unique advice.[/b] If someone has already covered your topic, make sure that your guide offers something different and/or better advice or else it may be downvoted by our community.[/li]\n[li][b]Extremely short guides may be better off as a comment.[/b] Though overall there is no predetermined length for a good guide.[/li]\n[li][b]We do not tolerate plagiarism in any form.[/b] Make sure to include credits to other sources and a hyperlink if you use their images or otherwise.[/li][/ul]',NULL),(NULL,NULL,0,'edit',0,2,'Any user can write a guide and then share it with the community. Before a guide will be available to the public, it will be put in a queue where it can be approved or rejected by the staff. We suggest that you make sure your guide is complete before you put it through this process. A complete guide will generally be thorough, 100% accurate for World of Warcraft\'s current build, and include details such as images.\n\n[h3]Tips For Creating Quality Guides[/h3]\n\n[ul][li][b]Use [url=?help=markup-guide]Aowow\'s BBCode[/url].[/b][/li]\n[li][b]Choose the correct category.[/b] Guides placed in the wrong category risk being rejected. Don\'t see your category? Email [feedback]![/li]\n[li][b]Always submit only complete guides.[/b] You can save in-progress ones indefinitely so you won\'t risk losing them.[/li]\n[li][b]Make sure it\'s on a unique topic with unique advice.[/b] If someone has already covered your topic, make sure that your guide offers something different and/or better advice or else it may be downvoted by our community.[/li]\n[li][b]Extremely short guides may be better off as a comment.[/b] Though overall there is no predetermined length for a good guide.[/li]\n[li][b]We do not tolerate plagiarism in any form.[/b] Make sure to include credits to other sources and a hyperlink if you use their images or otherwise.[/li][/ul]',NULL);
-/*!40000 ALTER TABLE `aowow_articles` ENABLE KEYS */;
-UNLOCK TABLES;
-
---
--- Dumping data for table `aowow_config`
---
-
-LOCK TABLES `aowow_config` WRITE;
-/*!40000 ALTER TABLE `aowow_config` DISABLE KEYS */;
-INSERT INTO `aowow_config` VALUES ('sql_limit_search','500',1,129,'default: 500 - max results for search'),('sql_limit_default','300',1,129,'default: 300 - max results for listviews'),('sql_limit_quicksearch','10',1,129,'default: 10 - max results for suggestions'),('sql_limit_none','0',1,129,'default: 0 - unlimited results (i wouldn\'t change that mate)'),('ttl_rss','60',1,129,'default: 60 - time to live for RSS (in seconds)'),('name','Aowow Database Viewer (ADV)',1,136,' - website title'),('name_short','Aowow',1,136,' - feed title'),('board_url','http://www.wowhead.com/forums?board=',1,136,' - another halfbaked javascript thing..'),('contact_email','feedback@aowow.org',1,136,' - displayed sender for auth-mails, ect'),('battlegroup','Pure Pwnage',1,136,' - pretend, we belong to a battlegroup to satisfy profiler-related Jscripts'),('debug','0',1,132,'default: 0 - disable cache, enable sql-errors, enable error_reporting'),('maintenance','1',1,132,'default: 0 - display brb gnomes and block access for non-staff'),('user_max_votes','50',1,129,'default: 50 - vote limit per day'),('force_ssl','0',1,132,'default: 0 - enforce SSL, if the server is behind a load balancer'),('locales','349',1,161,'default: 0x15D - allowed locales - 0:English, 2:French, 3:German, 4:Chinese, 6:Spanish, 8:Russian'),('screenshot_min_size','200',1,129,'default: 200 - minimum dimensions of uploaded screenshots in px (yes, it\'s square)'),('site_host','',1,136,' - points js to executable files'),('static_host','',1,136,' - points js to images & scripts'),('cache_decay','25200',2,129,'default: 60 * 60 * 7 - time to keep cache in seconds'),('cache_mode','1',2,161,'default: 1 - set cache method - 0:filecache, 1:memcached'),('cache_dir','',2,136,'default: cache/template - generated pages are saved here (requires CACHE_MODE: filecache)'),('acc_failed_auth_block','900',3,129,'default: 15 * 60 - how long an account is closed after exceeding FAILED_AUTH_COUNT (in seconds)'),('acc_failed_auth_count','5',3,129,'default: 5 - how often invalid passwords are tolerated'),('acc_allow_register','1',3,132,'default: 1 - allow/disallow account creation (requires AUTH_MODE: aowow)'),('acc_auth_mode','0',3,145,'default: 0 - source to auth against - 0:aowow, 1:TC auth-table, 2:external script'),('acc_create_save_decay','604800',3,129,'default: 604800 - time in wich an unconfirmed account cannot be overwritten by new registrations'),('acc_recovery_decay','300',3,129,'default: 300 - time to recover your account and new recovery requests are blocked'),('acc_ext_create_url','',3,136,'default: - if auth mode is not self; link to external account creation'),('acc_ext_recover_url','',3,136,'default: - if auth mode is not self; link to external account recovery'),('session_timeout_delay','3600',4,129,'default: 60 * 60 - non-permanent session times out in time() + X'),('session.gc_maxlifetime','604800',4,200,'default: 7*24*60*60 - lifetime of session data'),('session.gc_probability','1',4,200,'default: 0 - probability to remove session data on garbage collection'),('session.gc_divisor','100',4,200,'default: 100 - probability to remove session data on garbage collection'),('session_cache_dir','',4,136,'default: - php sessions are saved here. Leave empty to use php default directory.'),('rep_req_upvote','125',5,129,'default: 125 - required reputation to upvote comments'),('rep_req_downvote','250',5,129,'default: 250 - required reputation to downvote comments'),('rep_req_comment','75',5,129,'default: 75 - required reputation to write a comment'),('rep_req_reply','75',5,129,'default: 75 - required reputation to write a reply'),('rep_req_supervote','2500',5,129,'default: 2500 - required reputation for double vote effect'),('rep_req_votemore_base','2000',5,129,'default: 2000 - gains more votes past this threshold'),('rep_reward_register','100',5,129,'default: 100 - activated an account'),('rep_reward_upvoted','5',5,129,'default: 5 - comment received upvote'),('rep_reward_downvoted','0',5,129,'default: 0 - comment received downvote'),('rep_reward_good_report','10',5,129,'default: 10 - filed an accepted report'),('rep_reward_bad_report','0',5,129,'default: 0 - filed a rejected report'),('rep_reward_dailyvisit','5',5,129,'default: 5 - daily visit'),('rep_reward_user_warned','-50',5,129,'default: -50 - moderator imposed a warning'),('rep_reward_comment','1',5,129,'default: 1 - created a comment (not a reply) '),('rep_req_premium','25000',5,129,'default: 25000 - required reputation for premium status through reputation'),('rep_reward_upload','10',5,129,'default: 10 - suggested / uploaded video / screenshot was approved'),('rep_reward_article','100',5,129,'default: 100 - submitted an approved article/guide'),('rep_reward_user_suspended','-200',5,129,'default: -200 - moderator revoked rights'),('rep_req_votemore_add','250',5,129,'default: 250 - required reputation per additional vote past threshold'),('serialize_precision','5',0,65,' - some derelict code, probably unused'),('memory_limit','1500M',0,200,'default: 1500M - parsing spell.dbc is quite intense'),('default_charset','UTF-8',0,72,'default: UTF-8'),('analytics_user','',6,136,'default: - enter your GA-user here to track site stats'),('profiler_enable','0',7,132,'default: 0 - enable/disable profiler feature'),('profiler_queue_delay','3000',7,129,'default: 3000 - min. delay between queue cycles (in ms)'),('profiler_resync_ping','5000',7,129,'default: 5000 - how often the javascript asks for for updates, when queued (in ms)'),('profiler_resync_delay','3600',7,129,'default: 1*60*60 - how often a character can be refreshed (in sec)');
-/*!40000 ALTER TABLE `aowow_config` ENABLE KEYS */;
-UNLOCK TABLES;
-
---
--- Dumping data for table `aowow_dbversion`
---
-
-LOCK TABLES `aowow_dbversion` WRITE;
-/*!40000 ALTER TABLE `aowow_dbversion` DISABLE KEYS */;
-INSERT INTO `aowow_dbversion` VALUES (1645476215,0,NULL,NULL);
-/*!40000 ALTER TABLE `aowow_dbversion` ENABLE KEYS */;
-UNLOCK TABLES;
-
---
--- Dumping data for table `aowow_home_featuredbox`
---
-
-LOCK TABLES `aowow_home_featuredbox` WRITE;
-/*!40000 ALTER TABLE `aowow_home_featuredbox` DISABLE KEYS */;
-INSERT INTO `aowow_home_featuredbox` VALUES (1,NULL,0,0,0,0,'',NULL,NULL,'[pad]Welcome to [b][span class=q5]AoWoW[/span][/b]!','[pad]Bienvenue à [b][span class=q5]AoWoW[/span][/b]!','[pad]Willkommen bei [b][span class=q5]AoWoW[/span][/b]!','','','Добро[pad] пожаловать на [b][span class=q5]AoWoW[/span][/b]!'),(2,NULL,0,0,0,1,'STATIC_URL/images/logos/newsbox-explained.png',NULL,NULL,'[ul]\n[li][i]just demoing the newsbox here..[/i][/li]\n[li][b][url=http://www.example.com]..with urls[/url][/b][/li]\n[li][b]..typeLinks [item=45533][/b][/li]\n[li][b]..also, over there to the right is an overlay-trigger =>[/b][/li]\n[/ul]\n\n[ul]\n[li][tooltip name=demotip]hey, it hints you stuff![/tooltip][b][span class=tip tooltip=demotip]..hover me[/span][/b][/li]\n[/ul]','','','','','');
-/*!40000 ALTER TABLE `aowow_home_featuredbox` ENABLE KEYS */;
-UNLOCK TABLES;
-
---
--- Dumping data for table `aowow_home_featuredbox_overlay`
---
-
-LOCK TABLES `aowow_home_featuredbox_overlay` WRITE;
-/*!40000 ALTER TABLE `aowow_home_featuredbox_overlay` DISABLE KEYS */;
-INSERT INTO `aowow_home_featuredbox_overlay` VALUES (2,405,100,'http://example.com','example overlay','','','','','');
-/*!40000 ALTER TABLE `aowow_home_featuredbox_overlay` ENABLE KEYS */;
-UNLOCK TABLES;
-
---
--- Dumping data for table `aowow_home_titles`
---
-
-LOCK TABLES `aowow_home_titles` WRITE;
-/*!40000 ALTER TABLE `aowow_home_titles` DISABLE KEYS */;
-INSERT INTO `aowow_home_titles` VALUES (1,0,1522321542,1,0,'That\'s a 50 DKP plus!'),(2,0,1522321542,1,0,'We\'ve got what you need!'),(3,0,1522321542,1,0,'You haven\'t found the secret title yet.'),(4,0,1522321542,1,0,'...and knowing is half the battle!'),(5,0,1522321542,1,0,'Good news, everyone!'),(6,0,1522321542,1,0,'+1, Insightful'),(7,0,1522321542,1,0,'More effective than a [Booterang].'),(8,0,1522321542,1,0,'There is no cow level.'),(9,0,1522321542,1,0,'We\'ve got more style than a fashion designer who knows CSS.'),(10,0,1522321542,1,3,'Eure Fertigkeit in WoW hat sich auf 450 erhöht.'),(11,0,1522321542,1,0,'If you use your mouse to search, you won\'t be able to click on Rend.'),(12,0,1522321542,1,2,'Tout est dans l\'élégance.'),(13,0,1522321542,1,2,'Rend les chargements supportables depuis 2006.'),(14,0,1522321542,1,2,'Vous allez revenir.'),(15,0,1522321542,1,2,'Base de données extraordinaire'),(16,0,1522321542,1,2,'Si vous lisez ceci, arrêtez d\'appuyer sur F5.'),(17,0,1522321542,1,3,'Und der Tag ist gerettet.'),(18,0,1522321542,1,3,'Jetzt in allen bekannten Internetzen verfügbar!'),(19,0,1522321542,1,3,'Morgens, halb drei in Nordend'),(20,0,1522321542,1,3,'Macht auch Euren Webbrowser glücklich!'),(21,0,1522321542,1,3,'Hier findet Ihr sogar Mankriks Frau.'),(22,0,1522321542,1,6,'Base de datos extraordinaria de WoW'),(23,0,1522321542,1,6,'La única cosa en la que los ninjas y los piratas estan de acuerdo.'),(24,0,1522321542,1,6,'La elegancia lo es todo.'),(25,0,1522321542,1,6,'Hace feliz a los navegadores.'),(26,0,1522321542,1,8,'Ты ещё вернёшься.'),(27,0,1522321542,1,8,'Осваивание нового босса - 45 золота на ремонт. Персональный эпический предмет - 650 золотых'),(28,0,1522321542,1,8,'Не именной. Поделитесь им с друзьями!'),(29,0,1522321542,1,8,'Если вы здесь впервые, то вам необходимо воспользоваться поиском!'),(30,0,1522321542,1,8,'Приколы Мулгора без чата в Мулгоре.'),(31,0,1522321542,1,2,'Les trois premières lettres veulent tout dire.'),(32,0,1522321542,1,2,'Trouvez la femme de Mankrik grâce à lui.'),(33,0,1522321542,1,6,'Tu habilidad con WoW se ha incrementado a 450.'),(34,0,1522321542,1,6,'Buscando uno más: Tú'),(35,0,1522321542,1,8,'Первые три буквы говорят сами за себя.'),(36,0,1522321542,1,8,'У нас больше стиля, чем у дизайнера, знающего CSS.'),(37,0,1522321542,1,0,'Preventing wipes since 2006.'),(38,0,1522321542,1,0,'Never gonna give you up. Never gonna let you down.'),(39,0,1522321542,1,0,'The closest thing to an F1 key for WoW.'),(40,0,1522321542,1,2,'Non lié. Partagez-le avec vos amis !'),(41,0,1522321542,1,2,'Votre navigateur l\'adore !'),(42,0,1522321542,1,3,'Verhindert Wipes seit 2006.'),(43,0,1522321542,1,6,'+1, Utilidad'),(44,0,1522321542,1,6,'Épico, como tu líder de facción.'),(45,0,1522321542,1,8,'Он такой один...'),(46,0,1522321542,1,8,'Если вы это читаете, то прекратите обновлять страницу.'),(47,0,1522321542,1,0,'If you are reading this, stop pressing F5.'),(48,0,1522321542,1,2,'Chasse les jours pluvieux.'),(49,0,1522321542,1,3,'+1, Hilfreich'),(50,0,1522321542,1,3,'Episch - markant - dreifach verzaubert'),(51,0,1522321542,1,8,'Работает как положено.'),(52,0,1522321542,1,0,'Flagged for awesome.'),(53,0,1522321542,1,0,'Thrall-tested, Jaina-approved.'),(54,0,1522321542,1,8,'Всё дело в элегантности.'),(55,0,1522321542,1,0,'What does it mean?'),(56,0,1522321542,1,0,'YOU ARE NOW PREPARED!'),(57,0,1522321542,1,0,'srsly'),(58,0,1522321542,1,2,'C\'est comme prétendre être malade et aller à la plage, mais pour les bases de données.'),(59,0,1522321542,1,3,'Thrall-getestet, Jaina-genehmigt'),(60,0,1522321542,1,6,'Haciendo las pantallas de carga más soportables desde el 2006'),(61,0,1522321542,1,8,'Создан быть лидером.'),(62,0,1522321542,1,0,'You\'ll say \"Wow\" every time.'),(63,0,1522321542,1,0,'Dataz! We need more dataz!'),(64,0,1522321542,1,0,'Your skill in WoW has increased to 450.'),(65,0,1522321542,1,3,'Eleganz ist alles.'),(66,0,1522321542,1,8,'+1, Полезный'),(67,0,1522321542,1,8,'Ух ты!'),(68,0,1522321542,1,0,'Sometimes there is fire. You need to not be in it.'),(69,0,1522321542,1,0,'Working as intended.'),(70,0,1522321542,1,2,'La seule chose sur laquelle les ninjas et les pirates sont d\'accord.'),(71,0,1522321542,1,3,'Nicht seelengebunden. Teilt es mit Euren Freunden!'),(72,0,1522321542,1,8,'Теперь доступен во всех известных Интернетах!'),(73,0,1522321542,1,8,'Вы получаете добычу: [Легендарное Знание]'),(74,0,1522321542,1,0,'You\'ll be back.'),(75,0,1522321542,1,0,'Epic like your faction leader.'),(76,0,1522321542,1,3,'Manchmal gibt es Feuer. Ihr dürft nicht drin stehen.'),(77,0,1522321542,1,3,'Wer das hier lesen kann, drückt zu oft F5.'),(78,0,1522321542,1,6,'¡Datos! ¡Más Datos!'),(79,0,1522321542,1,8,'НЯМ НЯМ НЯМ'),(80,0,1522321542,1,2,'Testé par Thrall, approuvé par Jaina.'),(81,0,1522321542,1,8,'Сделайте его вашей новой расовой возможностью уже сегодня!'),(82,0,1522321542,1,0,'We do math, so you don\'t have to.'),(83,0,1522321542,1,0,'OM NOM NOM'),(84,0,1522321542,1,0,'Now available on all known internets!'),(85,0,1522321542,1,0,'We brake for dataz.'),(86,0,1522321542,1,3,'Neues von der Obstverkäuferfront'),(87,0,1522321542,1,6,'Las primeras tres palabras lo dicen todo.'),(88,0,1522321542,1,8,'Это как будто сказать всем, что ты болен, а самому пойти на пляж, - только для баз данных.'),(89,0,1522321542,1,8,'Меняем семечки на данные!'),(90,0,1522321542,1,0,'It\'s all about elegance.'),(91,0,1522321542,1,0,'Never underestimate the power of the Scout\'s code.'),(92,0,1522321542,1,6,'Elimina los días lluviosos.'),(93,0,1522321542,1,0,'You just won the game.'),(94,0,1522321542,1,8,'Данные! Нам надо больше данных!'),(95,0,1522321542,1,0,'WoW Database Extraordinaire'),(96,0,1522321542,1,0,'No longer soulbound. Can now be shared with friends!'),(97,0,1522321542,1,0,'The dataz you could be using.'),(98,0,1522321542,1,8,'Превосходен, как лидер вашей фракции.'),(99,0,1522321542,1,6,'¡Regresarás!');
-/*!40000 ALTER TABLE `aowow_home_titles` ENABLE KEYS */;
-UNLOCK TABLES;
-
---
--- Dumping data for table `aowow_loot_link`
---
-
-LOCK TABLES `aowow_loot_link` WRITE;
-/*!40000 ALTER TABLE `aowow_loot_link` DISABLE KEYS */;
-INSERT INTO `aowow_loot_link` VALUES (17537,185168,1,0),(18434,185169,1,0),(17536,185168,0,0),(18432,185169,0,0),(19218,184465,1,0),(21525,184849,1,0),(19710,184465,0,0),(21526,184849,0,0),(28234,190586,0,0),(-28234,193996,0,0),(27656,191349,0,0),(31561,193603,0,0),(26533,190663,0,0),(31217,193597,0,0),(16064,181366,0,692),(30603,193426,0,692),(16065,181366,0,692),(30601,193426,0,692),(30549,181366,1,692),(30600,193426,1,692),(16063,181366,0,692),(30602,193426,0,692),(28859,193905,0,0),(31734,193967,0,0),(32930,195046,0,0),(33909,195047,0,0),(32865,194313,0,0),(33147,194315,0,0),(33350,194957,0,0),(-33350,194958,0,0),(32845,194200,0,0),(32846,194201,0,0),(32906,194324,0,0),(33360,194325,0,0),(32871,194821,0,0),(33070,194822,0,0),(35119,195374,0,0),(35518,195375,0,0),(34928,195323,0,0),(35517,195324,0,0),(34705,195709,0,334),(36088,195710,0,334),(34702,195709,0,334),(36082,195710,0,334),(34701,195709,0,334),(36083,195710,0,334),(34657,195709,0,334),(36086,195710,0,334),(34703,195709,0,334),(36087,195710,0,334),(35572,195709,0,334),(36089,195710,0,334),(35569,195709,1,334),(36085,195710,1,334),(35571,195709,0,334),(36090,195710,0,334),(35570,195709,0,334),(36091,195710,0,334),(35617,195709,0,334),(36084,195710,0,334),(34441,195631,1,637),(34442,195632,1,637),(34443,195633,1,637),(35749,195635,1,637),(34444,195631,0,637),(35740,195632,0,637),(35741,195633,0,637),(-35741,195635,0,637),(34445,195631,0,637),(35705,195632,0,637),(35706,195633,0,637),(-35706,195635,0,637),(34447,195631,0,637),(35683,195632,0,637),(35684,195633,0,637),(-35684,195635,0,637),(34448,195631,0,637),(35724,195632,0,637),(35725,195633,0,637),(-35725,195635,0,637),(34449,195631,0,637),(35689,195632,0,637),(35690,195633,0,637),(-35690,195635,0,637),(34450,195631,0,637),(35695,195632,0,637),(35696,195633,0,637),(-35696,195635,0,637),(34451,195631,0,637),(35671,195632,0,637),(35672,195633,0,637),(-35672,195635,0,637),(34453,195631,0,637),(35718,195632,0,637),(35719,195633,0,637),(-35719,195635,0,637),(34454,195631,0,637),(35711,195632,0,637),(35712,195633,0,637),(-35712,195635,0,637),(34455,195631,0,637),(35680,195632,0,637),(35681,195633,0,637),(-35681,195635,0,637),(34456,195631,0,637),(35708,195632,0,637),(35709,195633,0,637),(-35709,195635,0,637),(34458,195631,0,637),(35692,195632,0,637),(35693,195633,0,637),(-35693,195635,0,637),(34459,195631,0,637),(35686,195632,0,637),(35687,195633,0,637),(-35687,195635,0,637),(34460,195631,0,637),(35702,195632,0,637),(35703,195633,0,637),(-35703,195635,0,637),(34461,195631,0,637),(35743,195632,0,637),(35744,195633,0,637),(-35744,195635,0,637),(34463,195631,0,637),(35734,195632,0,637),(35735,195633,0,637),(-35735,195635,0,637),(34465,195631,0,637),(35746,195632,0,637),(35747,195633,0,637),(-35747,195635,0,637),(34466,195631,0,637),(35665,195632,0,637),(35666,195633,0,637),(-35666,195635,0,637),(34467,195631,0,637),(35662,195632,0,637),(35663,195633,0,637),(-35663,195635,0,637),(34468,195631,0,637),(35721,195632,0,637),(35722,195633,0,637),(-35722,195635,0,637),(34469,195631,0,637),(35714,195632,0,637),(35715,195633,0,637),(-35715,195635,0,637),(34470,195631,0,637),(35728,195632,0,637),(35729,195633,0,637),(-35729,195635,0,637),(34471,195631,0,637),(35668,195632,0,637),(35669,195633,0,637),(-35669,195635,0,637),(34472,195631,0,637),(35699,195632,0,637),(35700,195633,0,637),(-35700,195635,0,637),(34473,195631,0,637),(35674,195632,0,637),(35675,195633,0,637),(-35675,195635,0,637),(34474,195631,0,637),(35731,195632,0,637),(35732,195633,0,637),(-35732,195635,0,637),(34475,195631,0,637),(35737,195632,0,637),(35738,195633,0,637),(-35738,195635,0,637),(37226,201710,0,0),(-37226,202336,0,0),(36948,202178,0,847),(38157,202180,0,847),(38639,202177,0,847),(38640,202179,0,847),(36939,202178,0,847),(38156,202180,0,847),(38637,202177,0,847),(38638,202179,0,847),(9034,169243,0,243),(9035,169243,1,243),(9036,169243,0,243),(9037,169243,0,243),(9038,169243,0,243),(9039,169243,0,243),(9040,169243,0,243),(37813,202238,0,0),(38402,202239,0,0),(38582,202240,0,0),(38583,202241,0,0),(36789,201959,0,0),(-36789,202338,0,0),(38174,202339,0,0),(-38174,202340,0,0);
-/*!40000 ALTER TABLE `aowow_loot_link` ENABLE KEYS */;
-UNLOCK TABLES;
-
---
--- Dumping data for table `aowow_profiler_excludes`
---
-
-LOCK TABLES `aowow_profiler_excludes` WRITE;
-/*!40000 ALTER TABLE `aowow_profiler_excludes` DISABLE KEYS */;
-INSERT INTO `aowow_profiler_excludes` VALUES (6,459,1,'Gray Wolf'),(6,468,1,'White Stallion'),(6,471,1,'Palamino'),(6,472,1,'Pinto'),(6,578,1,'Black Wolf'),(6,579,1,'Red Wolf'),(6,581,1,'Winter Wolf'),(6,3363,1,'Nether Drake'),(6,6896,1,'Black Ram'),(6,6897,1,'Blue Ram'),(6,8980,1,'Skeletal Horse'),(6,10681,1,'Summon Cockatoo'),(6,10686,1,'Summon Prairie Chicken'),(6,10687,1,'Summon White Plymouth Rock'),(6,10699,1,'Summon Bronze Whelpling'),(6,10700,1,'Summon Faeling'),(6,10701,1,'Summon Dart Frog'),(6,10702,1,'Summon Island Frog'),(6,10705,1,'Summon Eagle Owl'),(6,10708,1,'Summon Snowy Owl'),(6,10710,1,'Summon Cottontail Rabbit'),(6,10712,1,'Summon Spotted Rabbit'),(6,10715,1,'Summon Blue Racer'),(6,10718,1,'Green Water Snake'),(6,10719,1,'Ribbon Snake'),(6,10720,1,'Scarlet Snake'),(6,10721,1,'Summon Elven Wisp'),(6,10795,1,'Ivory Raptor'),(6,10798,1,'Obsidian Raptor'),(6,15648,1,'Corrupted Kitten'),(6,15779,1,'White Mechanostrider Mod B'),(6,15780,1,'Green Mechanostrider'),(6,15781,1,'Steel Mechanostrider'),(6,16055,1,'Black Nightsaber'),(6,16056,1,'Ancient Frostsaber'),(6,16058,1,'Primal Leopard'),(6,16059,1,'Tawny Sabercat'),(6,16060,1,'Golden Sabercat'),(6,16080,1,'Red Wolf'),(6,16081,1,'Winter Wolf'),(6,16082,1,'Palomino'),(6,16083,1,'White Stallion'),(6,16084,1,'Mottled Red Raptor'),(6,17450,1,'Ivory Raptor'),(6,17455,1,'Purple Mechanostrider'),(6,17456,1,'Red and Blue Mechanostrider'),(6,17458,1,'Fluorescent Green Mechanostrider'),(6,17459,1,'Icy Blue Mechanostrider Mod A'),(6,17460,1,'Frost Ram'),(6,17461,1,'Black Ram'),(6,17468,1,'Pet Fish'),(6,17469,1,'Pet Stone'),(6,18363,1,'Riding Kodo'),(6,18991,1,'Green Kodo'),(6,18992,1,'Teal Kodo'),(6,19363,1,'Summon Mechanical Yeti'),(6,23220,1,'Swift Dawnsaber'),(6,23428,1,'Albino Snapjaw'),(6,23429,1,'Loggerhead Snapjaw'),(6,23430,1,'Olive Snapjaw'),(6,23431,1,'Leatherback Snapjaw'),(6,23432,1,'Hawksbill Snapjaw'),(6,23530,16,'Tiny Red Dragon - wrong region'),(6,23531,16,'Tiny Green Dragon - wrong region'),(6,24985,1,'Summon Baby Murloc (Blue)'),(6,24986,1,'Summon Baby Murloc (Green)'),(6,24987,1,'Summon Baby Murloc (Orange)'),(6,24988,4,'Lurky - CE'),(6,24989,1,'Summon Baby Murloc (Pink)'),(6,24990,1,'Summon Baby Murloc (Purple)'),(6,25849,1,'Baby Shark'),(6,26067,1,'Summon Mechanical Greench'),(6,26391,1,'Tentacle Call'),(6,28828,1,'Nether Drake'),(6,29059,1,'Naxxramas Deathcharger'),(6,30152,1,'White Tiger Cub'),(6,30156,2,'Hippogryph Hatchling - TCG loot'),(6,30174,2,'Riding Turtle - TCG loot'),(6,32298,4,'Netherwhelp - CE'),(6,32345,1,'Peep the Phoenix Mount'),(6,33050,128,'Magical Crawdad'),(6,33057,1,'Summon Mighty Mr. Pinchy'),(6,33630,1,'Blue Mechanostrider'),(6,34407,1,'Great Elite Elekk'),(6,35157,1,'Summon Spotted Rabbit'),(6,37015,1,'Swift Nether Drake'),(6,40319,16,'Lucky - wrong region'),(6,40405,16,'Lucky - wrong region'),(6,43688,1,'Amani War Bear'),(6,43810,1,'Frost Wyrm'),(6,44317,1,'Merciless Nether Drake'),(6,44744,1,'Merciless Nether Drake'),(6,45125,2,'Rocket Chicken - TCG loot'),(6,45174,16,'Golden Pig - wrong region'),(6,45175,16,'Silver Pig - wrong region'),(6,45890,1,'Scorchling'),(6,47037,1,'Swift War Elekk'),(6,48406,16,'Essence of Competition - wrong region'),(6,48408,16,'Essence of Competition - wrong region'),(6,48954,8,'Swift Zhevra - promotion'),(6,49322,8,'Swift Zhevra - promotion'),(6,49378,1,'Brewfest Riding Kodo'),(6,50869,1,'Brewfest Kodo'),(6,50870,1,'Brewfest Ram'),(6,51851,1,'Vampiric Batling'),(6,51960,1,'Frost Wyrm Mount'),(6,52615,4,'Frosty - CE'),(6,53082,8,'Mini Tyrael - promotion'),(6,53768,1,'Haunted'),(6,54187,1,'Clockwork Rocket Bot'),(6,55068,1,'Mr. Chilly'),(6,58983,8,'Big Blizzard Bear - promotion'),(6,59572,1,'Black Polar Bear'),(6,59573,1,'Brown Polar Bear'),(6,59802,1,'Grand Ice Mammoth'),(6,59804,1,'Grand Ice Mammoth'),(6,59976,1,'Black Proto-Drake'),(6,60021,1,'Plagued Proto-Drake'),(6,60136,1,'Grand Caravan Mammoth'),(6,60140,1,'Grand Caravan Mammoth'),(6,61442,1,'Swift Mooncloth Carpet'),(6,61444,1,'Swift Shadoweave Carpet'),(6,61446,1,'Swift Spellfire Carpete'),(6,61855,1,'Baby Blizzard Bear'),(6,62048,1,'Black Dragonhawk Mount'),(6,62514,1,'Alarming Clockbot'),(6,63318,8,'Murkimus the Gladiator'),(6,64351,1,'XS-001 Constructor Bot'),(6,64656,1,'Blue Skeletal Warhorse'),(6,64731,128,'Sea Turtle - fishing only'),(6,65682,1,'Warbot'),(6,65917,2,'Magic Rooster - TCG loot'),(6,66030,8,'Grunty - promotion'),(6,66520,1,'Jade Tiger'),(6,66907,1,'Argent Warhorse'),(6,67527,16,'Onyx Panther - wrong region'),(6,68767,2,'Tuskarr Kite - TCG loot'),(6,68810,2,'Spectral Tiger Cub - TCG loot'),(6,69002,1,'Onyxian Whelpling'),(6,69452,8,'Core Hound Pup - promotion'),(6,69535,4,'Gryphon Hatchling - CE'),(6,69536,4,'Wind Rider Cub - CE'),(6,69539,1,'Zipao Tiger'),(6,69541,4,'Pandaren Monk - CE'),(6,69677,4,'Lil\' K.T. - CE'),(6,74856,2,'Blazing Hippogryph - TCG loot'),(6,74918,2,'Wooly White Rhino - TCG loot'),(6,75613,1,'Celestial Dragon'),(6,75614,1,'Celestial Steed - unavailable'),(6,75906,4,'Lil\' XT - CE'),(6,75936,1,'Murkimus the Gladiator'),(6,75973,8,'X-53 Touring Rocket - promotion'),(6,78381,8,'Mini Thor - promotion'),(8,87,1024,'Bloodsail Buccaneers - max rank is honored'),(8,92,1024,'Gelkis Clan Centaur - max rank is friendly'),(8,93,1024,'Magram Clan Centaur - max rank is friendly'),(6,46197,2,'X-51 Nether-Rocket - TCG loot'),(6,46199,2,'X-51 Nether-Rocket X-TREME - TCG loot'),(6,26656,1,'Black Qiraji Battle Tank - unavailable'),(6,43899,1,'Brewfest Ram - unavailable'),(6,49193,1,'Vengeful Nether Drake - unavailable'),(6,58615,1,'Brutal Nether Drake - unavailable'),(6,64927,1,'Deadly Gladiator\'s Frost Wyrm - unavailable'),(6,65439,1,'Furious Gladiator\'s Frost Wyrm - unavailable'),(6,67336,1,'Relentless Gladiator\'s Frost Wyrm - unavailable'),(6,71810,1,'Wrathful Gladiator\'s Frost Wyrm - unavailable'),(11,122,1,'RealmFirst Kel\'T Title - unavailable'),(11,159,1,'RealmFirst Algalon Title - unavailable'),(11,120,1,'RealmFirst Maly Title - unavailable'),(11,170,1,'RealmFirst TotGC Title - unavailable'),(11,139,1,'RealmFirst Sarth Title - unavailable'),(11,158,1,'RealmFirst Yogg Title - unavailable'),(6,28505,8,'Poley - promotion'),(6,28487,1,'Terky - unavailable'),(8,70,1024,'Syndicate - max rank is neutral'),(6,28242,1,'Icebane Breastplate'),(6,28243,1,'Icebane Gauntlets'),(6,28244,1,'Icebane Bracers'),(6,16986,1,'Blood Talon'),(6,16987,1,'Darkspear'),(6,16965,1,'Bleakwood Hew'),(6,8366,1,'Ironforge Chain'),(6,8368,1,'Ironforge Gauntlets'),(6,9942,1,'Mithril Scale Gloves'),(6,2671,1,'Rough Bronze Bracers'),(6,16980,1,'Rune Edge'),(6,16960,1,'Thorium Greatsword'),(6,16967,1,'Inlaid Thorium Hammer'),(6,30342,1,'Red Smoke Flare'),(6,30343,1,'Blue Smoke Flare'),(6,28205,1,'Glacial Gloves'),(6,28207,1,'Glacial Vest'),(6,28208,1,'Glacial Cloak'),(6,28209,1,'Glacial Wrists'),(6,28222,1,'Icy Scale Breastplate'),(6,28223,1,'Icy Scale Gauntlets'),(6,28224,1,'Icy Scale Bracers'),(6,28219,1,'Polar Tunic'),(6,28220,1,'Polar Gloves'),(6,28221,1,'Polar Bracers'),(6,28021,1,'Arcane Dust'),(6,44612,1,'Enchant Gloves - Greater Blasting'),(6,62257,1,'Enchant Weapon - Titanguard'),(6,31461,1,'Heavy Netherweave Net'),(6,56048,1,'Duskweave Boots'),(6,7636,1,'Green Woolen Robe'),(6,8778,1,'Boots of Darkness'),(6,12062,1,'Stormcloth Pants'),(6,12063,1,'Stormcloth Gloves'),(6,12068,1,'Stormcloth Vest'),(6,12083,1,'Stormcloth Headband'),(6,12087,1,'Stormcloth Shoulders'),(6,12090,1,'Stormcloth Boots');
-/*!40000 ALTER TABLE `aowow_profiler_excludes` ENABLE KEYS */;
-UNLOCK TABLES;
-
---
--- Dumping data for table `aowow_setup_custom_data`
---
-
-LOCK TABLES `aowow_setup_custom_data` WRITE;
-/*!40000 ALTER TABLE `aowow_setup_custom_data` DISABLE KEYS */;
-INSERT INTO `aowow_setup_custom_data` VALUES ('zones',2257,'cuFlags','0','Deeprun Tram - make visible'),('zones',2257,'category','0','Deeprun Tram - Category: Eastern Kingdoms'),('zones',2257,'type','1','Deeprun Tram - Type: Transit'),('zones',3698,'expansion','1','Nagrand Arena - Addon: BC'),('zones',3702,'expansion','1','Blades Edge Arena - Addon: BC'),('zones',3968,'expansion','1','Ruins of Lordaeron Arena - Addon: BC'),('zones',4378,'expansion','1','Dalaran Arena - Addon: WotLK'),('zones',4406,'expansion','1','Ring of Valor Arena - Addon: WotLK'),('zones',2597,'maxPlayer','40','Alterac Valey - Players: 40 [battlemasterlist.dbc: 5]'),('zones',4710,'maxPlayer','40','Isle of Conquest - Players: 40 [battlemasterlist.dbc: 5]'),('zones',3849,'parentAreaId','3523','The Mechanar - Parent: Netherstorm [not set in map.dbc]'),('zones',3849,'parentX','87.3','The Mechanar - Entrance xPos'),('zones',3849,'parentY','51.1','The Mechanar - Entrance yPos'),('zones',3847,'parentAreaId','3523','The Botanica - Parent: Netherstorm [not set in map.dbc]'),('zones',3847,'parentX','71.7','The Botanica - Entrance xPos'),('zones',3847,'parentY','55.1','The Botanica - Entrance yPos'),('zones',3848,'parentAreaId','3523','The Arcatraz - Parent: Netherstorm [not set in map.dbc]'),('zones',3848,'parentX','74.3','The Arcatraz - Entrance xPos'),('zones',3848,'parentY','57.8','The Arcatraz - Entrance yPos'),('zones',3845,'parentAreaId','3523','Tempest Keep - Parent: Netherstorm [not set in map.dbc]'),('zones',3845,'parentX','73.5','Tempest Keep - Entrance xPos'),('zones',3845,'parentY','63.7','Tempest Keep - Entrance yPos'),('zones',3456,'parentAreaId','65','Naxxramas - Parent: Netherstorm [not set in map.dbc]'),('zones',3456,'parentX','87.3','Naxxramas - Entrance xPos'),('zones',3456,'parentY','87.3','Naxxramas - Entrance yPos'),('zones',4893,'parentAreaId','4812','The Frost Queen\'s Lair - Parent: Icecrown Citadel'),('zones',4894,'parentAreaId','4812','Putricide\'s Laboratory [..] - Parent: Icecrown Citadel'),('zones',4895,'parentAreaId','4812','The Crimson Hall - Parent: Icecrown Citadel'),('zones',4896,'parentAreaId','4812','The Frozen Throne - Parent: Icecrown Citadel'),('zones',4897,'parentAreaId','4812','The Sanctum of Blood - Parent: Icecrown Citadel'),('zones',4893,'cuFlags','1073741824','The Frost Queen\'s Lair - set: CUSTOM_EXCLUDE_FOR_LISTVIEW'),('zones',4894,'cuFlags','1073741824','Putricide\'s Laboratory [..] - set: CUSTOM_EXCLUDE_FOR_LISTVIEW'),('achievement',1956,'itemExtra','44738','Higher Learning - item rewarded through gossip'),('zones',4895,'cuFlags','1073741824','The Crimson Hall - set: CUSTOM_EXCLUDE_FOR_LISTVIEW'),('titles',137,'gender','2','Matron - female'),('zones',4896,'cuFlags','1073741824','The Frozen Throne - set: CUSTOM_EXCLUDE_FOR_LISTVIEW'),('zones',4897,'cuFlags','1073741824','The Sanctum of Blood - set: CUSTOM_EXCLUDE_FOR_LISTVIEW'),('zones',4076,'cuFlags','1073741824','Reuse Me 7 - set: CUSTOM_EXCLUDE_FOR_LISTVIEW'),('zones',207,'cuFlags','1073741824','The Great Sea - set: CUSTOM_EXCLUDE_FOR_LISTVIEW'),('zones',208,'cuFlags','1073741824','Unused Ironcladcove - set: CUSTOM_EXCLUDE_FOR_LISTVIEW'),('zones',2817,'levelMin','74','Crystalsong Forest - missing lfgDungeons entry'),('zones',1477,'cuFlags','1073741824','The Temple of Atal\'Hakkar - set: CUSTOM_EXCLUDE_FOR_LISTVIEW'),('zones',41,'levelMin','50','Deadwind Pass - missing lfgDungeons entry'),('zones',41,'levelMax','60','Deadwind Pass - missing lfgDungeons entry'),('zones',2257,'levelMin','1','Deeprun Tram - missing lfgDungeons entry'),('zones',2257,'levelMax','80','Deeprun Tram - missing lfgDungeons entry'),('zones',4298,'category','0','Plaguelands: The Scarlet Enclave - Parent: Eastern Kingdoms'),('zones',4298,'levelMin','55','Plaguelands: The Scarlet Enclave - missing lfgDungeons entry'),('zones',4298,'levelMax','58','Plaguelands: The Scarlet Enclave - missing lfgDungeons entry'),('zones',493,'levelMin','15','Moonglade - missing lfgDungeons entry'),('zones',493,'levelMax','60','Moonglade - missing lfgDungeons entry'),('zones',2817,'levelMax','76','Crystalsong Forest - missing lfgDungeons entry'),('zones',4742,'levelMin','77','Hrothgar\'s Landing - missing lfgDungeons entry'),('zones',4742,'levelMax','80','Hrothgar\'s Landing - missing lfgDungeons entry'),('classes',8,'roles','4','Mage - rngDPS'),('classes',2,'roles','11','Paladin - mleDPS + Tank + Heal'),('classes',3,'roles','4','Hunter - rngDPS'),('classes',4,'roles','2','Rogue - mleDPS'),('classes',5,'roles','5','Priest - rngDPS + Heal'),('classes',6,'roles','10','Death Knight - mleDPS + Tank'),('classes',7,'roles','7','Shaman - mleDPS + rngDPS + Heal'),('classes',8,'roles','4','Mage - rngDPS'),('classes',8,'roles','4','Mage - rngDPS'),('classes',8,'roles','4','Mage - rngDPS'),('currencies',103,'cap','10000','Arena Points - cap'),('currencies',104,'cap','75000','Honor Points - cap'),('currencies',1,'cuFlags','1073741824','Currency Token Test Token 1 - set: CUSTOM_EXCLUDE_FOR_LISTVIEW'),('currencies',2,'cuFlags','1073741824','Currency Token Test Token 2 - set: CUSTOM_EXCLUDE_FOR_LISTVIEW'),('currencies',4,'cuFlags','1073741824','Currency Token Test Token 3 - set: CUSTOM_EXCLUDE_FOR_LISTVIEW'),('currencies',22,'cuFlags','1073741824','Birmingham Test Item 3 - set: CUSTOM_EXCLUDE_FOR_LISTVIEW'),('currencies',141,'cuFlags','1073741824','zzzOLDDaily Quest Faction Token - set: CUSTOM_EXCLUDE_FOR_LISTVIEW'),('currencies',1,'category','3','Currency Token Test Token 1 - category: unused'),('currencies',2,'category','3','Currency Token Test Token 2 - category: unused'),('currencies',4,'category','3','Currency Token Test Token 3 - category: unused'),('currencies',22,'category','3','Birmingham Test Item 3 - category: unused'),('currencies',141,'category','3','zzzOLDDaily Quest Faction Token - category: unused'),('factions',68,'qmNpcIds','33555','Undercity - set Quartermaster'),('factions',47,'qmNpcIds','33310','Ironforge - set Quartermaster'),('factions',69,'qmNpcIds','33653','Darnassus - set Quartermaster'),('factions',72,'qmNpcIds','33307','Stormwind - set Quartermaster'),('factions',76,'qmNpcIds','33553','Orgrimmar - set Quartermaster'),('factions',81,'qmNpcIds','33556','Thunder Bluff - set Quartermaster'),('factions',922,'qmNpcIds','16528','Tranquillien - set Quartermaster'),('factions',930,'qmNpcIds','33657','Exodar - set Quartermaster'),('factions',932,'qmNpcIds','19321','The Aldor - set Quartermaster'),('factions',933,'qmNpcIds','20242 23007','The Consortium - set Quartermaster'),('factions',935,'qmNpcIds','21432','The Sha\'tar - set Quartermaster'),('factions',941,'qmNpcIds','20241','The Mag\'har - set Quartermaster'),('factions',942,'qmNpcIds','17904','Cenarion Expedition - set Quartermaster'),('factions',946,'qmNpcIds','17657','Honor Hold - set Quartermaster'),('factions',947,'qmNpcIds','17585','Thrallmar - set Quartermaster'),('factions',970,'qmNpcIds','18382','Sporeggar - set Quartermaster'),('factions',978,'qmNpcIds','20240','Kurenai - set Quartermaster'),('factions',989,'qmNpcIds','21643','Keepers of Time - set Quartermaster'),('factions',1011,'qmNpcIds','21655','Lower City - set Quartermaster'),('factions',1012,'qmNpcIds','23159','Ashtongue Deathsworn - set Quartermaster'),('factions',1037,'qmNpcIds','32773 32564','Alliance Vanguard - set Quartermaster'),('factions',1038,'qmNpcIds','23428','Ogri\'la - set Quartermaster'),('factions',1052,'qmNpcIds','32774 32565','Horde Expedition - set Quartermaster'),('factions',1073,'qmNpcIds','31916 32763','The Kalu\'ak - set Quartermaster'),('factions',1090,'qmNpcIds','32287','Kirin Tor - set Quartermaster'),('factions',1091,'qmNpcIds','32533','The Wyrmrest Accord - set Quartermaster'),('factions',1094,'qmNpcIds','34881','The Silver Covenant - set Quartermaster'),('factions',1105,'qmNpcIds','31910','The Oracles - set Quartermaster'),('factions',1106,'qmNpcIds','30431','Argent Crusade - set Quartermaster'),('factions',1119,'qmNpcIds','32540','The Sons of Hodir - set Quartermaster'),('factions',1124,'qmNpcIds','34772','The Sunreavers - set Quartermaster'),('factions',1156,'qmNpcIds','37687','The Ashen Verdict - set Quartermaster'),('factions',1082,'cuFlags','1073741824','REUSE - set: CUSTOM_EXCLUDE_FOR_LISTVIEW'),('factions',952,'cuFlags','1073741824','Test Faction 3 - set: CUSTOM_EXCLUDE_FOR_LISTVIEW'),('titles',138,'gender','1','Patron - male'),('sounds',15407,'cat','10','UR_Algalon_Summon03 - is not an item pickup'),('shapeshiftforms',1,'displayIdH','8571','Cat Form - spellshapeshiftform.dbc missing displayId'),('shapeshiftforms',15,'displayIdH','8571','Creature - Cat - spellshapeshiftform.dbc missing displayId'),('shapeshiftforms',5,'displayIdH','2289','Bear Form - spellshapeshiftform.dbc missing displayId'),('shapeshiftforms',8,'displayIdH','2289','Dire Bear Form - spellshapeshiftform.dbc missing displayId'),('shapeshiftforms',14,'displayIdH','2289','Creature - Bear - spellshapeshiftform.dbc missing displayId'),('shapeshiftforms',27,'displayIdH','21244','Flight Form, Epic - spellshapeshiftform.dbc missing displayId'),('shapeshiftforms',29,'displayIdH','20872','Flight Form - spellshapeshiftform.dbc missing displayId'),('races',1,'leader','29611','Human - King Varian Wrynn'),('races',1,'factionId','72','Human - Stormwind'),('races',1,'startAreaId','12','Human - Elwynn Forest'),('races',2,'leader','4949','Orc - Thrall'),('races',2,'factionId','76','Orc - Orgrimmar'),('races',2,'startAreaId','14','Orc - Durotar'),('races',3,'leader','2784','Dwarf - King Magni Bronzebeard'),('races',3,'factionId','47','Dwarf - Ironforge'),('races',3,'startAreaId','1','Dwarf - Dun Morogh'),('races',4,'leader','7999','Night Elf - Tyrande Whisperwind'),('races',4,'factionId','69','Night Elf - Darnassus'),('races',4,'startAreaId','141','Night Elf - Teldrassil'),('races',5,'leader','10181','Undead - Lady Sylvanas Windrunner'),('races',5,'factionId','68','Undead - Undercity'),('races',5,'startAreaId','85','Undead - Tirisfal Glades'),('races',6,'leader','3057','Tauren - Cairne Bloodhoof'),('races',6,'factionId','81','Tauren - Thunder Bluff'),('races',6,'startAreaId','215','Tauren - Mulgore'),('races',7,'leader','7937','Gnome - High Tinker Mekkatorque'),('races',7,'factionId','54','Gnome - Gnomeregan Exiles'),('races',7,'startAreaId','1','Gnome - Dun Morogh'),('races',8,'leader','10540','Troll - Vol\'jin'),('races',8,'factionId','530','Troll - Darkspear Trolls'),('races',8,'startAreaId','14','Troll - Durotar'),('races',10,'leader','16802','Blood Elf - Lor\'themar Theron'),('races',10,'factionId','911','Blood Elf - Silvermoon City'),('races',10,'startAreaId','3430','Blood Elf - Eversong Woods'),('races',11,'leader','17468','Draenei - Prophet Velen'),('races',11,'factionId','930','Draenei - Exodar'),('races',11,'startAreaId','3524','Draenei - Azuremyst Isle'),('holidays',62,'iconString','inv_misc_missilelarge_red','Fireworks Spectacular'),('holidays',141,'iconString','calendar_winterveilstart','Feast of Winter Veil'),('holidays',181,'iconString','calendar_noblegardenstart','Noblegarden'),('holidays',201,'iconString','calendar_childrensweekstart','Children\'s Week'),('holidays',283,'iconString','inv_jewelry_necklace_21','Call to Arms: Alterac Valley'),('holidays',284,'iconString','inv_misc_rune_07','Call to Arms: Warsong Gulch'),('holidays',285,'iconString','inv_jewelry_amulet_07','Call to Arms: Arathi Basin'),('holidays',301,'iconString','calendar_fishingextravaganzastart','Stranglethorn Fishing Extravaganza'),('holidays',321,'iconString','calendar_harvestfestivalstart','Harvest Festival'),('holidays',324,'iconString','calendar_hallowsendstart','Hallow\'s End'),('holidays',327,'iconString','calendar_lunarfestivalstart','Lunar Festival'),('holidays',335,'iconString','calendar_loveintheairstart','Love is in the Air'),('holidays',341,'iconString','calendar_midsummerstart','Midsummer Fire Festival'),('holidays',353,'iconString','spell_nature_eyeofthestorm','Call to Arms: Eye of the Storm'),('holidays',372,'iconString','calendar_brewfeststart','Brewfest'),('holidays',374,'iconString','calendar_darkmoonfaireelwynnstart','Darkmoon Faire'),('holidays',375,'iconString','calendar_darkmoonfairemulgorestart','Darkmoon Faire'),('holidays',376,'iconString','calendar_darkmoonfaireterokkarstart','Darkmoon Faire'),('holidays',398,'iconString','calendar_piratesdaystart','Pirates\' Day'),('holidays',400,'iconString','achievement_bg_winsoa','Call to Arms: Strand of the Ancients'),('holidays',404,'iconString','calendar_harvestfestivalstart','Pilgrim\'s Bounty'),('holidays',406,'iconString','achievement_boss_lichking','Wrath of the Lich King Launch'),('holidays',409,'iconString','calendar_dayofthedeadstart','Day of the Dead'),('holidays',420,'iconString','achievement_bg_winwsg','Call to Arms: Isle of Conquest'),('holidays',423,'iconString','calendar_loveintheairstart','Love is in the Air'),('holidays',424,'iconString','calendar_fishingextravaganzastart','Kalu\'ak Fishing Derby'),('holidays',141,'achievementCatOrId','156','Feast of Winter Veil - Category: Feast of Winter Veil'),('holidays',181,'achievementCatOrId','159','Noblegarden - Category: Noblegarden'),('holidays',201,'achievementCatOrId','163','Children\'s Week - Category: Children\'s Week'),('holidays',324,'achievementCatOrId','158','Hallow\'s End - Category: Hallow\'s End'),('holidays',327,'achievementCatOrId','160','Lunar Festival - Category: Lunar Festival'),('holidays',341,'achievementCatOrId','161','Midsummer Fire Festival - Category: Midsummer Fire Festival'),('holidays',372,'achievementCatOrId','162','Brewfest - Category: Brewfest'),('holidays',398,'achievementCatOrId','-3457','Pirates\' Day - Achievement: The Captain\'s Booty'),('holidays',404,'achievementCatOrId','14981','Pilgrim\'s Bounty - Category: Pilgrim\'s Bounty'),('holidays',409,'achievementCatOrId','-3456','Day of the Dead - Achievement: Dead Man\'s Party'),('holidays',423,'achievementCatOrId','187','Love is in the Air - Category: Love is in the Air'),('holidays',324,'bossCreature','23682','Hallow\'s End - Headless Horseman'),('holidays',327,'bossCreature','15467','Lunar Festival - Omen'),('holidays',341,'bossCreature','25740','Midsummer Fire Festival - Ahune'),('holidays',372,'bossCreature','23872','Brewfest - Coren Direbrew'),('holidays',423,'bossCreature','36296','Love is in the Air - Apothecary Hummel'),('skillline',197,'professionMask','512','Tailoring'),('skillline',186,'professionMask','256','Mining'),('skillline',165,'specializations','10656 10658 10660','Leatherworking'),('skillline',165,'recipeSubClass','1','Leatherworking'),('skillline',165,'professionMask','128','Leatherworking'),('skillline',755,'recipeSubClass','10','Jewelcrafting'),('skillline',755,'professionMask','64','Jewelcrafting'),('skillline',129,'recipeSubClass','7','First Aid'),('skillline',129,'professionMask','32','First Aid'),('skillline',202,'specializations','20219 20222','Engineering'),('skillline',202,'recipeSubClass','3','Engineering'),('skillline',202,'professionMask','16','Engineering'),('skillline',333,'recipeSubClass','8','Enchanting'),('skillline',333,'professionMask','8','Enchanting'),('skillline',185,'recipeSubClass','5','Cooking'),('skillline',185,'professionMask','4','Cooking'),('skillline',164,'specializations','9788 9787 17041 17040 17039','Blacksmithing'),('skillline',164,'recipeSubClass','4','Blacksmithing'),('skillline',164,'professionMask','2','Blacksmithing'),('skillline',171,'specializations','28677 28675 28672','Alchemy'),('skillline',171,'recipeSubClass','6','Alchemy'),('skillline',171,'professionMask','1','Alchemy'),('skillline',393,'professionMask','0','Skinning'),('skillline',197,'recipeSubClass','2','Tailoring'),('skillline',197,'specializations','26798 26801 26797','Tailoring'),('skillline',356,'professionMask','1024','Fishing'),('skillline',356,'recipeSubClass','9','Fishing'),('skillline',182,'professionMask','2048','Herbalism'),('skillline',773,'professionMask','4096','Inscription'),('skillline',773,'recipeSubClass','11','Inscription'),('skillline',785,'name_loc0','Pet - Wasp','Pet - Wasp'),('skillline',781,'name_loc2','Familier - diablosaure exotique','Pet - Exotic Devlisaur'),('skillline',758,'name_loc6','Mascota: Evento - Control remoto','Pet - Event - Remote Control'),('skillline',758,'name_loc3','Tier - Ereignis Ferngesteuert','Pet - Event - Remote Control'),('skillline',758,'categoryId','7','Pet - Event - Remote Control - bring in line with other pets'),('skillline',788,'categoryId','7','Pet - Exotic Spirit Beast - bring in line with other pets'),('item',33147,'class','9','Formula: Enchant Cloak - Subtlety - Class: Recipes'),('item',33147,'subClass','8','Formula: Enchant Cloak - Subtlety - Subclass: Enchanting');
-/*!40000 ALTER TABLE `aowow_setup_custom_data` 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 */;
-/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;
-
--- Dump completed on 2018-03-26 16:36:07
diff --git a/setup/setup.php b/setup/setup.php
index 7e58dc1d..029eb864 100644
--- a/setup/setup.php
+++ b/setup/setup.php
@@ -1,5 +1,7 @@
false]);
- if ($dbc->error)
- {
- CLI::write('CLISetup::loadDBC() - required DBC '.$n.'.dbc not found!', CLI::LOG_ERROR);
- return false;
- }
-
- if (!$dbc->readFile())
- {
- CLI::write('CLISetup::loadDBC() - DBC '.$n.'.dbc could not be written to DB!', CLI::LOG_ERROR);
- return false;
- }
- }
- break;
-}
+fwrite(STDOUT, "\n");
+exit;
?>
diff --git a/setup/sql/01-db_structure.sql b/setup/sql/01-db_structure.sql
new file mode 100644
index 00000000..86c69286
--- /dev/null
+++ b/setup/sql/01-db_structure.sql
@@ -0,0 +1,3257 @@
+-- MariaDB dump 10.19 Distrib 10.4.32-MariaDB, for Win64 (AMD64)
+--
+-- Host: localhost Database: aowow
+-- ------------------------------------------------------
+-- Server version 10.4.32-MariaDB
+
+/*!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_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;
+/*!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' */;
+/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;
+
+--
+-- Table structure for table `aowow_account`
+--
+
+DROP TABLE IF EXISTS `aowow_account`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `aowow_account` (
+ `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
+ `extId` int(10) unsigned DEFAULT NULL COMMENT 'external user id',
+ `login` varchar(64) NOT NULL DEFAULT '' COMMENT 'only used for login',
+ `passHash` varchar(128) NOT NULL,
+ `username` varchar(64) NOT NULL COMMENT 'unique; used for for links and display',
+ `email` varchar(64) DEFAULT NULL COMMENT 'unique; can be used for login if AUTH_SELF and can be NULL if not',
+ `joinDate` int(10) unsigned NOT NULL COMMENT 'unixtime',
+ `dailyVotes` smallint(5) unsigned NOT NULL DEFAULT 0,
+ `consecutiveVisits` smallint(5) unsigned NOT NULL DEFAULT 0,
+ `curIP` varchar(45) NOT NULL DEFAULT '',
+ `prevIP` varchar(45) NOT NULL DEFAULT '',
+ `curLogin` int(10) unsigned NOT NULL DEFAULT 0 COMMENT 'unixtime',
+ `prevLogin` int(10) unsigned NOT NULL DEFAULT 0,
+ `locale` tinyint(3) unsigned NOT NULL DEFAULT 0 COMMENT '0,2,3,4,6,8',
+ `userGroups` smallint(5) unsigned NOT NULL DEFAULT 0 COMMENT 'bitmask',
+ `debug` tinyint(1) NOT NULL DEFAULT 0 COMMENT 'show ids in lists user option',
+ `avatar` tinyint(4) DEFAULT 0,
+ `avatarborder` tinyint(3) unsigned NOT NULL DEFAULT 2,
+ `wowicon` varchar(55) NOT NULL DEFAULT '' COMMENT 'iconname as avatar',
+ `title` varchar(50) NOT NULL DEFAULT '' COMMENT 'user can obtain custom titles',
+ `description` text NOT NULL DEFAULT '',
+ `excludeGroups` smallint(5) unsigned NOT NULL DEFAULT 1 COMMENT 'profiler - exclude bitmask',
+ `userPerms` tinyint(3) unsigned NOT NULL DEFAULT 0 COMMENT 'bool isAdmin',
+ `status` tinyint(3) unsigned NOT NULL DEFAULT 0 COMMENT 'flag, see defines',
+ `statusTimer` int(10) unsigned NOT NULL DEFAULT 0,
+ `token` varchar(40) DEFAULT NULL COMMENT 'identification key for changes to account',
+ `updateValue` varchar(128) DEFAULT NULL COMMENT 'temp store for new passHash / email',
+ `renameCooldown` int(10) unsigned NOT NULL DEFAULT 0 COMMENT 'timestamp when rename is available again',
+ PRIMARY KEY (`id`),
+ UNIQUE KEY `username` (`username`),
+ UNIQUE KEY `email` (`email`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=COMPACT;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Table structure for table `aowow_account_avatars`
+--
+
+DROP TABLE IF EXISTS `aowow_account_avatars`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `aowow_account_avatars` (
+ `id` mediumint(8) unsigned NOT NULL,
+ `userId` int(10) unsigned NOT NULL,
+ `name` varchar(20) NOT NULL,
+ `size` mediumint(8) unsigned NOT NULL,
+ `when` int(10) unsigned NOT NULL,
+ `current` tinyint(3) unsigned NOT NULL DEFAULT 0,
+ `status` tinyint(3) unsigned NOT NULL DEFAULT 0,
+ UNIQUE KEY `id` (`id`) USING BTREE,
+ KEY `userId` (`userId`) USING BTREE,
+ CONSTRAINT `FK_acc_avatars` FOREIGN KEY (`userId`) REFERENCES `aowow_account` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=COMPACT;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Table structure for table `aowow_account_banned`
+--
+
+DROP TABLE IF EXISTS `aowow_account_banned`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `aowow_account_banned` (
+ `id` int(10) unsigned NOT NULL,
+ `userId` int(10) unsigned NOT NULL COMMENT 'affected accountId',
+ `staffId` int(10) unsigned NOT NULL COMMENT 'executive accountId',
+ `typeMask` tinyint(3) unsigned NOT NULL COMMENT 'ACC_BAN_*',
+ `start` int(10) unsigned NOT NULL COMMENT 'unixtime',
+ `end` int(10) unsigned NOT NULL COMMENT 'automatic unban @ unixtime',
+ `reason` varchar(255) NOT NULL,
+ PRIMARY KEY (`id`),
+ KEY `FK_acc_banned` (`userId`),
+ CONSTRAINT `FK_acc_banned` FOREIGN KEY (`userId`) REFERENCES `aowow_account` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=COMPACT;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Table structure for table `aowow_account_bannedips`
+--
+
+DROP TABLE IF EXISTS `aowow_account_bannedips`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `aowow_account_bannedips` (
+ `ip` varchar(45) NOT NULL,
+ `type` tinyint(4) NOT NULL COMMENT '0: onSignin; 1:onSignup',
+ `count` smallint(6) NOT NULL COMMENT 'nFails',
+ `unbanDate` int(11) NOT NULL COMMENT 'automatic remove @ unixtime',
+ PRIMARY KEY (`ip`,`type`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Table structure for table `aowow_account_cookies`
+--
+
+DROP TABLE IF EXISTS `aowow_account_cookies`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `aowow_account_cookies` (
+ `userId` int(10) unsigned NOT NULL,
+ `name` varchar(127) NOT NULL,
+ `data` text NOT NULL,
+ UNIQUE KEY `userId_name` (`userId`,`name`) USING BTREE,
+ KEY `userId` (`userId`) USING BTREE,
+ CONSTRAINT `FK_acc_cookies` FOREIGN KEY (`userId`) REFERENCES `aowow_account` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=COMPACT;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Table structure for table `aowow_account_excludes`
+--
+
+DROP TABLE IF EXISTS `aowow_account_excludes`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `aowow_account_excludes` (
+ `userId` int(10) unsigned NOT NULL,
+ `type` smallint(5) unsigned NOT NULL,
+ `typeId` mediumint(8) unsigned NOT NULL,
+ `mode` enum('EXCLUDE','INCLUDE') NOT NULL,
+ UNIQUE KEY `userId_type_typeId` (`userId`,`type`,`typeId`),
+ KEY `userId` (`userId`),
+ CONSTRAINT `FK_acc_excludes` FOREIGN KEY (`userId`) REFERENCES `aowow_account` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Table structure for table `aowow_account_favorites`
+--
+
+DROP TABLE IF EXISTS `aowow_account_favorites`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `aowow_account_favorites` (
+ `userId` int(10) unsigned NOT NULL,
+ `type` smallint(5) unsigned NOT NULL,
+ `typeId` mediumint(8) unsigned NOT NULL,
+ UNIQUE KEY `userId_type_typeId` (`userId`,`type`,`typeId`),
+ KEY `userId` (`userId`),
+ CONSTRAINT `FK_acc_favorites` FOREIGN KEY (`userId`) REFERENCES `aowow_account` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Table structure for table `aowow_account_profiles`
+--
+
+DROP TABLE IF EXISTS `aowow_account_profiles`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `aowow_account_profiles` (
+ `accountId` int(10) unsigned NOT NULL,
+ `profileId` int(10) unsigned NOT NULL,
+ `extraFlags` int(10) unsigned NOT NULL DEFAULT 0,
+ UNIQUE KEY `accountId_profileId` (`accountId`,`profileId`),
+ KEY `accountId` (`accountId`),
+ KEY `profileId` (`profileId`),
+ CONSTRAINT `FK_account_id` FOREIGN KEY (`accountId`) REFERENCES `aowow_account` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
+ CONSTRAINT `FK_profile_id` FOREIGN KEY (`profileId`) REFERENCES `aowow_profiler_profiles` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Table structure for table `aowow_account_reputation`
+--
+
+DROP TABLE IF EXISTS `aowow_account_reputation`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `aowow_account_reputation` (
+ `userId` int(10) unsigned NOT NULL,
+ `action` tinyint(3) unsigned NOT NULL COMMENT 'e.g. upvote a comment',
+ `amount` tinyint(3) NOT NULL,
+ `sourceA` int(10) unsigned NOT NULL DEFAULT 0 COMMENT 'e.g. upvoting user',
+ `sourceB` int(10) unsigned NOT NULL DEFAULT 0 COMMENT 'e.g. upvoted commentId',
+ `date` int(10) unsigned NOT NULL DEFAULT 0,
+ UNIQUE KEY `userId_action_source` (`userId`,`action`,`sourceA`,`sourceB`),
+ KEY `userId` (`userId`),
+ CONSTRAINT `FK_acc_rep` FOREIGN KEY (`userId`) REFERENCES `aowow_account` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=COMPACT COMMENT='reputation log';
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Table structure for table `aowow_account_sessions`
+--
+
+DROP TABLE IF EXISTS `aowow_account_sessions`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `aowow_account_sessions` (
+ `userId` int(10) unsigned NOT NULL,
+ `sessionId` varchar(190) NOT NULL COMMENT 'PHPSESSID',
+ `created` int(10) unsigned NOT NULL,
+ `expires` int(10) unsigned NOT NULL COMMENT 'timestamp or 0 (never expires)',
+ `touched` int(10) unsigned NOT NULL COMMENT 'timestamp - last used',
+ `deviceInfo` varchar(256) NOT NULL,
+ `ip` varchar(45) NOT NULL COMMENT 'can change; just last used ip',
+ `status` enum('ACTIVE','LOGOUT','FORCEDLOGOUT','EXPIRED') NOT NULL,
+ UNIQUE KEY `sessionId` (`sessionId`) USING BTREE,
+ KEY `userId` (`userId`) USING BTREE,
+ CONSTRAINT `FK_acc_sessions` FOREIGN KEY (`userId`) REFERENCES `aowow_account` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=COMPACT;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Table structure for table `aowow_account_weightscale_data`
+--
+
+DROP TABLE IF EXISTS `aowow_account_weightscale_data`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `aowow_account_weightscale_data` (
+ `id` int(11) NOT NULL,
+ `field` varchar(15) NOT NULL,
+ `val` smallint(5) unsigned NOT NULL,
+ KEY `id` (`id`),
+ CONSTRAINT `FK_acc_weightscales` FOREIGN KEY (`id`) REFERENCES `aowow_account_weightscales` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Table structure for table `aowow_account_weightscales`
+--
+
+DROP TABLE IF EXISTS `aowow_account_weightscales`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `aowow_account_weightscales` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `userId` int(10) unsigned NOT NULL,
+ `name` varchar(32) NOT NULL,
+ `class` tinyint(3) unsigned NOT NULL DEFAULT 0,
+ `orderIdx` tinyint(3) unsigned NOT NULL DEFAULT 0 COMMENT 'check how Profiler handles classes with more than 3 specs before modifying',
+ `icon` varchar(51) NOT NULL DEFAULT '',
+ PRIMARY KEY (`id`),
+ KEY `FK_acc_weights` (`userId`),
+ CONSTRAINT `FK_acc_weights` FOREIGN KEY (`userId`) REFERENCES `aowow_account` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=COMPACT;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Table structure for table `aowow_achievement`
+--
+
+DROP TABLE IF EXISTS `aowow_achievement`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `aowow_achievement` (
+ `id` smallint(5) unsigned NOT NULL,
+ `faction` tinyint(3) unsigned NOT NULL,
+ `map` smallint(6) NOT NULL,
+ `chainId` tinyint(3) unsigned NOT NULL DEFAULT 0,
+ `chainPos` tinyint(3) unsigned NOT NULL DEFAULT 0,
+ `category` smallint(5) unsigned NOT NULL DEFAULT 0,
+ `parentCat` smallint(6) NOT NULL DEFAULT 0,
+ `points` tinyint(3) unsigned NOT NULL DEFAULT 0,
+ `orderInGroup` tinyint(3) unsigned NOT NULL DEFAULT 0,
+ `iconId` smallint(5) unsigned NOT NULL DEFAULT 0,
+ `iconIdBak` mediumint(8) unsigned NOT NULL DEFAULT 0,
+ `flags` smallint(5) unsigned NOT NULL DEFAULT 0,
+ `reqCriteriaCount` tinyint(3) unsigned NOT NULL DEFAULT 0,
+ `refAchievement` smallint(5) unsigned NOT NULL DEFAULT 0,
+ `itemExtra` mediumint(8) unsigned DEFAULT NULL,
+ `cuFlags` int(10) unsigned NOT NULL DEFAULT 0 COMMENT 'see defines.php for flags',
+ `name_loc0` varchar(78) DEFAULT NULL,
+ `name_loc2` varchar(79) DEFAULT NULL,
+ `name_loc3` varchar(86) DEFAULT NULL,
+ `name_loc4` varchar(86) DEFAULT NULL,
+ `name_loc6` varchar(78) DEFAULT NULL,
+ `name_loc8` varchar(76) DEFAULT NULL,
+ `description_loc0` text DEFAULT NULL,
+ `description_loc2` text DEFAULT NULL,
+ `description_loc3` text DEFAULT NULL,
+ `description_loc4` text DEFAULT NULL,
+ `description_loc6` text DEFAULT NULL,
+ `description_loc8` text DEFAULT NULL,
+ `reward_loc0` varchar(74) DEFAULT NULL,
+ `reward_loc2` varchar(88) DEFAULT NULL,
+ `reward_loc3` varchar(92) DEFAULT NULL,
+ `reward_loc4` varchar(92) DEFAULT NULL,
+ `reward_loc6` varchar(83) DEFAULT NULL,
+ `reward_loc8` varchar(95) DEFAULT NULL,
+ PRIMARY KEY (`id`),
+ KEY `iconId` (`iconId`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Table structure for table `aowow_achievementcategory`
+--
+
+DROP TABLE IF EXISTS `aowow_achievementcategory`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `aowow_achievementcategory` (
+ `id` smallint(5) unsigned NOT NULL DEFAULT 0,
+ `parentCat` smallint(6) NOT NULL DEFAULT 0,
+ `parentCat2` smallint(6) NOT NULL DEFAULT 0,
+ PRIMARY KEY (`id`) USING BTREE
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Table structure for table `aowow_announcements`
+--
+
+DROP TABLE IF EXISTS `aowow_announcements`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `aowow_announcements` (
+ `id` int(11) NOT NULL AUTO_INCREMENT COMMENT 'iirc negative Ids cant be deleted',
+ `page` varchar(256) NOT NULL,
+ `name` varchar(256) NOT NULL,
+ `groupMask` smallint(5) unsigned NOT NULL,
+ `style` varchar(256) NOT NULL,
+ `mode` tinyint(3) unsigned NOT NULL COMMENT '0:pageTop; 1:contentTop',
+ `status` tinyint(3) unsigned NOT NULL COMMENT '0:disabled; 1:enabled; 2:deleted',
+ `text_loc0` text DEFAULT NULL,
+ `text_loc2` text DEFAULT NULL,
+ `text_loc3` text DEFAULT NULL,
+ `text_loc4` text DEFAULT NULL,
+ `text_loc6` text DEFAULT NULL,
+ `text_loc8` text DEFAULT NULL,
+ PRIMARY KEY (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Table structure for table `aowow_areatrigger`
+--
+
+DROP TABLE IF EXISTS `aowow_areatrigger`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `aowow_areatrigger` (
+ `id` int(10) unsigned NOT NULL,
+ `cuFlags` int(10) unsigned NOT NULL DEFAULT 0 COMMENT 'see defines.php for flags',
+ `type` smallint(5) unsigned NOT NULL,
+ `mapId` smallint(5) unsigned NOT NULL COMMENT 'world pos. from dbc',
+ `posX` float NOT NULL COMMENT 'world pos. from dbc',
+ `posY` float NOT NULL COMMENT 'world pos. from dbc',
+ `orientation` float NOT NULL,
+ `name` varchar(100) DEFAULT NULL,
+ `quest` mediumint(8) unsigned DEFAULT NULL,
+ PRIMARY KEY (`id`),
+ KEY `quest` (`quest`),
+ KEY `type` (`type`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Table structure for table `aowow_articles`
+--
+
+DROP TABLE IF EXISTS `aowow_articles`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `aowow_articles` (
+ `type` smallint(6) DEFAULT NULL,
+ `typeId` mediumint(9) DEFAULT NULL,
+ `locale` tinyint(3) unsigned NOT NULL,
+ `url` varchar(50) DEFAULT NULL,
+ `rev` tinyint(3) unsigned NOT NULL DEFAULT 0,
+ `editAccess` smallint(5) unsigned NOT NULL DEFAULT 2,
+ `article` mediumtext DEFAULT NULL COMMENT 'Markdown formated',
+ UNIQUE KEY `type` (`type`,`typeId`,`locale`,`rev`),
+ UNIQUE KEY `url` (`url`,`locale`,`rev`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Table structure for table `aowow_classes`
+--
+
+DROP TABLE IF EXISTS `aowow_classes`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `aowow_classes` (
+ `id` int(11) NOT NULL,
+ `fileString` varchar(128) DEFAULT NULL,
+ `iconId` smallint(5) unsigned NOT NULL DEFAULT 0,
+ `name_loc0` varchar(128) DEFAULT NULL,
+ `name_loc2` varchar(128) DEFAULT NULL,
+ `name_loc3` varchar(128) DEFAULT NULL,
+ `name_loc4` varchar(128) DEFAULT NULL,
+ `name_loc6` varchar(128) DEFAULT NULL,
+ `name_loc8` varchar(128) DEFAULT NULL,
+ `powerType` tinyint(4) NOT NULL DEFAULT 0,
+ `raceMask` int(11) NOT NULL DEFAULT 0,
+ `roles` int(11) NOT NULL DEFAULT 0,
+ `skills` varchar(32) NOT NULL DEFAULT '',
+ `flags` mediumint(9) NOT NULL DEFAULT 0,
+ `cuFlags` int(10) unsigned NOT NULL DEFAULT 0 COMMENT 'see defines.php for flags',
+ `weaponTypeMask` int(11) NOT NULL DEFAULT 0,
+ `armorTypeMask` int(11) NOT NULL DEFAULT 0,
+ `expansion` tinyint(4) NOT NULL DEFAULT 0,
+ PRIMARY KEY (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Table structure for table `aowow_comments`
+--
+
+DROP TABLE IF EXISTS `aowow_comments`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `aowow_comments` (
+ `id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT 'Comment ID',
+ `type` smallint(5) unsigned NOT NULL DEFAULT 0 COMMENT 'Type of Page',
+ `typeId` mediumint(9) NOT NULL DEFAULT 0 COMMENT 'ID Of Page',
+ `userId` int(10) unsigned DEFAULT NULL COMMENT 'User ID',
+ `roles` smallint(5) unsigned NOT NULL,
+ `body` text NOT NULL COMMENT 'Comment text',
+ `date` int(11) NOT NULL COMMENT 'Comment timestap',
+ `flags` smallint(6) NOT NULL DEFAULT 0 COMMENT 'deleted, outofdate, sticky',
+ `replyTo` int(10) unsigned NOT NULL DEFAULT 0 COMMENT 'Reply To, comment ID',
+ `editUserId` int(10) unsigned NOT NULL DEFAULT 0 COMMENT 'Last Edit User ID',
+ `editDate` int(10) unsigned NOT NULL DEFAULT 0 COMMENT 'Last Edit Time',
+ `editCount` tinyint(3) unsigned NOT NULL DEFAULT 0 COMMENT 'Count Of Edits',
+ `deleteUserId` int(10) unsigned NOT NULL DEFAULT 0,
+ `deleteDate` int(10) unsigned NOT NULL DEFAULT 0,
+ `responseUserId` int(10) unsigned NOT NULL DEFAULT 0,
+ `responseBody` text DEFAULT NULL,
+ `responseRoles` smallint(5) unsigned NOT NULL DEFAULT 0,
+ PRIMARY KEY (`id`),
+ KEY `type_typeId` (`type`,`typeId`),
+ KEY `FK_acc_co` (`userId`),
+ CONSTRAINT `FK_acc_co` FOREIGN KEY (`userId`) REFERENCES `aowow_account` (`id`) ON DELETE SET NULL ON UPDATE CASCADE
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Table structure for table `aowow_config`
+--
+
+DROP TABLE IF EXISTS `aowow_config`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `aowow_config` (
+ `key` varchar(50) NOT NULL,
+ `value` varchar(255) NOT NULL,
+ `default` varchar(255) DEFAULT NULL,
+ `cat` tinyint(3) unsigned NOT NULL DEFAULT 0,
+ `flags` smallint(5) unsigned NOT NULL DEFAULT 0,
+ `comment` varchar(255) NOT NULL DEFAULT '',
+ PRIMARY KEY (`key`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Table structure for table `aowow_creature`
+--
+
+DROP TABLE IF EXISTS `aowow_creature`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `aowow_creature` (
+ `id` mediumint(8) unsigned NOT NULL DEFAULT 0,
+ `cuFlags` int(10) unsigned NOT NULL DEFAULT 0 COMMENT 'see defines.php for flags',
+ `difficultyEntry1` mediumint(8) unsigned NOT NULL DEFAULT 0,
+ `difficultyEntry2` mediumint(8) unsigned NOT NULL DEFAULT 0,
+ `difficultyEntry3` mediumint(8) unsigned NOT NULL DEFAULT 0,
+ `KillCredit1` int(10) unsigned NOT NULL DEFAULT 0,
+ `KillCredit2` int(10) unsigned NOT NULL DEFAULT 0,
+ `displayId1` mediumint(8) unsigned NOT NULL DEFAULT 0,
+ `displayId2` mediumint(8) unsigned NOT NULL DEFAULT 0,
+ `displayId3` mediumint(8) unsigned NOT NULL DEFAULT 0,
+ `displayId4` mediumint(8) unsigned NOT NULL DEFAULT 0,
+ `textureString` varchar(50) DEFAULT NULL,
+ `modelId` mediumint(9) NOT NULL DEFAULT 0,
+ `humanoid` tinyint(3) unsigned NOT NULL DEFAULT 0,
+ `iconString` varchar(50) DEFAULT NULL COMMENT 'first texture of first model for search (up to 11 other skins omitted..)',
+ `name_loc0` varchar(100) DEFAULT NULL,
+ `name_loc2` varchar(100) DEFAULT NULL,
+ `name_loc3` varchar(100) DEFAULT NULL,
+ `name_loc4` varchar(100) DEFAULT NULL,
+ `name_loc6` varchar(100) DEFAULT NULL,
+ `name_loc8` varchar(100) DEFAULT NULL,
+ `subname_loc0` varchar(100) DEFAULT NULL,
+ `subname_loc2` varchar(100) DEFAULT NULL,
+ `subname_loc3` varchar(100) DEFAULT NULL,
+ `subname_loc4` varchar(100) DEFAULT NULL,
+ `subname_loc6` varchar(100) DEFAULT NULL,
+ `subname_loc8` varchar(100) DEFAULT NULL,
+ `minLevel` tinyint(3) unsigned NOT NULL DEFAULT 1,
+ `maxLevel` tinyint(3) unsigned NOT NULL DEFAULT 1,
+ `exp` smallint(6) NOT NULL DEFAULT 0,
+ `faction` smallint(5) unsigned NOT NULL DEFAULT 0,
+ `npcflag` int(10) unsigned NOT NULL DEFAULT 0,
+ `rank` tinyint(3) unsigned NOT NULL DEFAULT 0,
+ `dmgSchool` tinyint(4) NOT NULL DEFAULT 0,
+ `dmgMultiplier` float NOT NULL DEFAULT 1,
+ `atkSpeed` int(10) unsigned NOT NULL DEFAULT 0,
+ `rngAtkSpeed` int(10) unsigned NOT NULL DEFAULT 0,
+ `mleVariance` float NOT NULL DEFAULT 1,
+ `rngVariance` float NOT NULL DEFAULT 1,
+ `unitClass` tinyint(3) unsigned NOT NULL DEFAULT 0,
+ `unitFlags` int(10) unsigned NOT NULL DEFAULT 0,
+ `unitFlags2` int(10) unsigned NOT NULL DEFAULT 0,
+ `dynamicFlags` int(10) unsigned NOT NULL DEFAULT 0,
+ `family` tinyint(4) NOT NULL DEFAULT 0,
+ `trainerType` tinyint(4) NOT NULL DEFAULT 0,
+ `trainerRequirement` smallint(5) unsigned NOT NULL DEFAULT 0,
+ `dmgMin` float unsigned NOT NULL DEFAULT 0,
+ `dmgMax` float unsigned NOT NULL DEFAULT 0,
+ `mleAtkPwrMin` smallint(5) unsigned NOT NULL DEFAULT 0,
+ `mleAtkPwrMax` smallint(5) unsigned NOT NULL DEFAULT 0,
+ `rngAtkPwrMin` smallint(5) unsigned NOT NULL DEFAULT 0,
+ `rngAtkPwrMax` smallint(5) unsigned NOT NULL DEFAULT 0,
+ `type` tinyint(3) unsigned NOT NULL DEFAULT 0,
+ `typeFlags` int(10) unsigned NOT NULL DEFAULT 0,
+ `lootId` mediumint(8) unsigned NOT NULL DEFAULT 0,
+ `pickpocketLootId` mediumint(8) unsigned NOT NULL DEFAULT 0,
+ `skinLootId` mediumint(8) unsigned NOT NULL DEFAULT 0,
+ `spell1` mediumint(8) unsigned NOT NULL DEFAULT 0,
+ `spell2` mediumint(8) unsigned NOT NULL DEFAULT 0,
+ `spell3` mediumint(8) unsigned NOT NULL DEFAULT 0,
+ `spell4` mediumint(8) unsigned NOT NULL DEFAULT 0,
+ `spell5` mediumint(8) unsigned NOT NULL DEFAULT 0,
+ `spell6` mediumint(8) unsigned NOT NULL DEFAULT 0,
+ `spell7` mediumint(8) unsigned NOT NULL DEFAULT 0,
+ `spell8` mediumint(8) unsigned NOT NULL DEFAULT 0,
+ `petSpellDataId` mediumint(8) unsigned NOT NULL DEFAULT 0,
+ `vehicleId` mediumint(8) unsigned NOT NULL DEFAULT 0,
+ `minGold` mediumint(8) unsigned NOT NULL DEFAULT 0,
+ `maxGold` mediumint(8) unsigned NOT NULL DEFAULT 0,
+ `healthMin` int(10) unsigned NOT NULL DEFAULT 1,
+ `healthMax` int(10) unsigned NOT NULL DEFAULT 1,
+ `manaMin` int(10) unsigned NOT NULL DEFAULT 1,
+ `manaMax` int(10) unsigned NOT NULL DEFAULT 1,
+ `armorMin` mediumint(8) unsigned NOT NULL DEFAULT 1,
+ `armorMax` mediumint(8) unsigned NOT NULL DEFAULT 1,
+ `resistance1` smallint(6) NOT NULL DEFAULT 0,
+ `resistance2` smallint(6) NOT NULL DEFAULT 0,
+ `resistance3` smallint(6) NOT NULL DEFAULT 0,
+ `resistance4` smallint(6) NOT NULL DEFAULT 0,
+ `resistance5` smallint(6) NOT NULL DEFAULT 0,
+ `resistance6` smallint(6) NOT NULL DEFAULT 0,
+ `racialLeader` tinyint(3) unsigned NOT NULL DEFAULT 0,
+ `mechanicImmuneMask` int(10) unsigned NOT NULL DEFAULT 0,
+ `schoolImmuneMask` int(10) unsigned NOT NULL DEFAULT 0,
+ `flagsExtra` int(10) unsigned NOT NULL DEFAULT 0,
+ `ScriptOrAI` varchar(64) DEFAULT NULL,
+ `StringId` varchar(64) DEFAULT NULL,
+ PRIMARY KEY (`id`),
+ KEY `difficultyEntry1` (`difficultyEntry1`),
+ KEY `difficultyEntry2` (`difficultyEntry2`),
+ KEY `difficultyEntry3` (`difficultyEntry3`),
+ KEY `idx_loot` (`lootId`),
+ KEY `idx_pickpocketloot` (`pickpocketLootId`),
+ KEY `idx_skinloot` (`skinLootId`),
+ KEY `idx_trainer` (`trainerType`),
+ KEY `idx_trainerrequirement` (`trainerRequirement`),
+ KEY `idx_name0` (`name_loc0`),
+ KEY `idx_name2` (`name_loc2`),
+ KEY `idx_name3` (`name_loc3`),
+ KEY `idx_name4` (`name_loc4`),
+ KEY `idx_name6` (`name_loc6`),
+ KEY `idx_name8` (`name_loc8`),
+ KEY `idx_spell1` (`spell1`),
+ KEY `idx_spell2` (`spell2`),
+ KEY `idx_spell3` (`spell3`),
+ KEY `idx_spell4` (`spell4`),
+ KEY `idx_spell5` (`spell5`),
+ KEY `idx_spell6` (`spell6`),
+ KEY `idx_spell7` (`spell7`),
+ KEY `idx_spell8` (`spell8`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Table structure for table `aowow_creature_search`
+--
+
+DROP TABLE IF EXISTS `aowow_creature_search`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `aowow_creature_search` (
+ `id` mediumint(8) unsigned NOT NULL,
+ `locale` tinyint(3) unsigned NOT NULL,
+ `nName` varchar(100) DEFAULT NULL,
+ `nSubname` varchar(100) DEFAULT NULL,
+ PRIMARY KEY (`id`,`locale`),
+ FULLTEXT KEY `idx_ft_na` (`nName`),
+ FULLTEXT KEY `idx_ft_na_ex` (`nName`,`nSubname`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Table structure for table `aowow_creature_sounds`
+--
+
+DROP TABLE IF EXISTS `aowow_creature_sounds`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `aowow_creature_sounds` (
+ `id` smallint(5) unsigned NOT NULL COMMENT 'CreatureDisplayInfo.dbc/id',
+ `greeting` smallint(5) unsigned NOT NULL DEFAULT 0,
+ `farewell` smallint(5) unsigned NOT NULL DEFAULT 0,
+ `angry` smallint(5) unsigned NOT NULL DEFAULT 0,
+ `exertion` smallint(5) unsigned NOT NULL DEFAULT 0,
+ `exertioncritical` smallint(5) unsigned NOT NULL DEFAULT 0,
+ `injury` smallint(5) unsigned NOT NULL DEFAULT 0,
+ `injurycritical` smallint(5) unsigned NOT NULL DEFAULT 0,
+ `death` smallint(5) unsigned NOT NULL DEFAULT 0,
+ `stun` smallint(5) unsigned NOT NULL DEFAULT 0,
+ `stand` smallint(5) unsigned NOT NULL DEFAULT 0,
+ `footstep` smallint(5) unsigned NOT NULL DEFAULT 0,
+ `aggro` smallint(5) unsigned NOT NULL DEFAULT 0,
+ `wingflap` smallint(5) unsigned NOT NULL DEFAULT 0,
+ `wingglide` smallint(5) unsigned NOT NULL DEFAULT 0,
+ `alert` smallint(5) unsigned NOT NULL DEFAULT 0,
+ `fidget` smallint(5) unsigned NOT NULL DEFAULT 0,
+ `customattack` smallint(5) unsigned NOT NULL DEFAULT 0,
+ `loop` smallint(5) unsigned NOT NULL DEFAULT 0,
+ `jumpstart` smallint(5) unsigned NOT NULL DEFAULT 0,
+ `jumpend` smallint(5) unsigned NOT NULL DEFAULT 0,
+ `petattack` smallint(5) unsigned NOT NULL DEFAULT 0,
+ `petorder` smallint(5) unsigned NOT NULL DEFAULT 0,
+ `petdismiss` smallint(5) unsigned NOT NULL DEFAULT 0,
+ `birth` smallint(5) unsigned NOT NULL DEFAULT 0,
+ `spellcast` smallint(5) unsigned NOT NULL DEFAULT 0,
+ `submerge` smallint(5) unsigned NOT NULL DEFAULT 0,
+ `submerged` smallint(5) unsigned NOT NULL DEFAULT 0,
+ `transform` smallint(5) unsigned NOT NULL DEFAULT 0,
+ `transformanimated` smallint(5) unsigned NOT NULL DEFAULT 0,
+ PRIMARY KEY (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='!ATTENTION!\r\nthe primary key of this table is NOT a creatureId, but displayId\r\n\r\ncolumn names from LANG.sound_activities';
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Table structure for table `aowow_creature_waypoints`
+--
+
+DROP TABLE IF EXISTS `aowow_creature_waypoints`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `aowow_creature_waypoints` (
+ `creatureOrPath` int(11) NOT NULL,
+ `point` smallint(5) unsigned NOT NULL,
+ `areaId` smallint(5) unsigned NOT NULL,
+ `floor` tinyint(4) NOT NULL DEFAULT -1,
+ `posX` float unsigned NOT NULL,
+ `posY` float unsigned NOT NULL,
+ `wait` int(10) unsigned NOT NULL DEFAULT 0,
+ PRIMARY KEY (`creatureOrPath`,`point`,`areaId`,`floor`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Table structure for table `aowow_currencies`
+--
+
+DROP TABLE IF EXISTS `aowow_currencies`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `aowow_currencies` (
+ `id` int(11) NOT NULL,
+ `category` mediumint(9) NOT NULL,
+ `cuFlags` int(10) unsigned NOT NULL DEFAULT 0 COMMENT 'see defines.php for flags',
+ `iconId` smallint(5) unsigned NOT NULL DEFAULT 0,
+ `itemId` int(11) NOT NULL DEFAULT 0,
+ `cap` int(10) unsigned NOT NULL DEFAULT 0,
+ `name_loc0` varchar(64) DEFAULT NULL,
+ `name_loc2` varchar(64) DEFAULT NULL,
+ `name_loc3` varchar(64) DEFAULT NULL,
+ `name_loc4` varchar(64) DEFAULT NULL,
+ `name_loc6` varchar(64) DEFAULT NULL,
+ `name_loc8` varchar(64) DEFAULT NULL,
+ `description_loc0` varchar(256) DEFAULT NULL,
+ `description_loc2` varchar(256) DEFAULT NULL,
+ `description_loc3` varchar(256) DEFAULT NULL,
+ `description_loc4` varchar(256) DEFAULT NULL,
+ `description_loc6` varchar(256) DEFAULT NULL,
+ `description_loc8` varchar(256) DEFAULT NULL,
+ PRIMARY KEY (`id`),
+ KEY `iconId` (`iconId`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Table structure for table `aowow_dbversion`
+--
+
+DROP TABLE IF EXISTS `aowow_dbversion`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `aowow_dbversion` (
+ `date` int(10) unsigned NOT NULL DEFAULT 0,
+ `part` tinyint(3) unsigned NOT NULL DEFAULT 0,
+ `sql` text DEFAULT NULL,
+ `build` text DEFAULT NULL
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Table structure for table `aowow_declinedword`
+--
+
+DROP TABLE IF EXISTS `aowow_declinedword`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `aowow_declinedword` (
+ `id` smallint(5) unsigned NOT NULL,
+ `word` varchar(127) NOT NULL,
+ PRIMARY KEY (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Table structure for table `aowow_declinedwordcases`
+--
+
+DROP TABLE IF EXISTS `aowow_declinedwordcases`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `aowow_declinedwordcases` (
+ `wordId` smallint(5) unsigned NOT NULL,
+ `caseIdx` tinyint(3) unsigned NOT NULL,
+ `word` varchar(131) NOT NULL,
+ PRIMARY KEY (`wordId`,`caseIdx`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Table structure for table `aowow_emotes`
+--
+
+DROP TABLE IF EXISTS `aowow_emotes`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `aowow_emotes` (
+ `id` smallint(6) NOT NULL,
+ `cmd` varchar(35) NOT NULL,
+ `isAnimated` tinyint(3) unsigned NOT NULL DEFAULT 0,
+ `flags` smallint(5) unsigned NOT NULL DEFAULT 0,
+ `parentEmote` smallint(6) NOT NULL DEFAULT 0,
+ `soundId` smallint(6) NOT NULL DEFAULT 0,
+ `state` tinyint(3) unsigned NOT NULL DEFAULT 0,
+ `stateParam` tinyint(3) unsigned NOT NULL DEFAULT 0,
+ `cuFlags` int(10) unsigned NOT NULL DEFAULT 0 COMMENT 'see defines.php for flags',
+ `extToExt_loc0` varchar(150) DEFAULT NULL,
+ `extToExt_loc2` varchar(150) DEFAULT NULL,
+ `extToExt_loc3` varchar(150) DEFAULT NULL,
+ `extToExt_loc4` varchar(150) DEFAULT NULL,
+ `extToExt_loc6` varchar(150) DEFAULT NULL,
+ `extToExt_loc8` varchar(150) DEFAULT NULL,
+ `extToMe_loc0` varchar(150) DEFAULT NULL,
+ `extToMe_loc2` varchar(150) DEFAULT NULL,
+ `extToMe_loc3` varchar(150) DEFAULT NULL,
+ `extToMe_loc4` varchar(150) DEFAULT NULL,
+ `extToMe_loc6` varchar(150) DEFAULT NULL,
+ `extToMe_loc8` varchar(150) DEFAULT NULL,
+ `meToExt_loc0` varchar(150) DEFAULT NULL,
+ `meToExt_loc2` varchar(150) DEFAULT NULL,
+ `meToExt_loc3` varchar(150) DEFAULT NULL,
+ `meToExt_loc4` varchar(150) DEFAULT NULL,
+ `meToExt_loc6` varchar(150) DEFAULT NULL,
+ `meToExt_loc8` varchar(150) DEFAULT NULL,
+ `extToNone_loc0` varchar(150) DEFAULT NULL,
+ `extToNone_loc2` varchar(150) DEFAULT NULL,
+ `extToNone_loc3` varchar(150) DEFAULT NULL,
+ `extToNone_loc4` varchar(150) DEFAULT NULL,
+ `extToNone_loc6` varchar(150) DEFAULT NULL,
+ `extToNone_loc8` varchar(150) DEFAULT NULL,
+ `meToNone_loc0` varchar(150) DEFAULT NULL,
+ `meToNone_loc2` varchar(150) DEFAULT NULL,
+ `meToNone_loc3` varchar(150) DEFAULT NULL,
+ `meToNone_loc4` varchar(150) DEFAULT NULL,
+ `meToNone_loc6` varchar(150) DEFAULT NULL,
+ `meToNone_loc8` varchar(150) DEFAULT NULL,
+ PRIMARY KEY (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Table structure for table `aowow_emotes_aliasses`
+--
+
+DROP TABLE IF EXISTS `aowow_emotes_aliasses`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `aowow_emotes_aliasses` (
+ `id` smallint(5) unsigned NOT NULL,
+ `locales` smallint(5) unsigned NOT NULL,
+ `command` varchar(20) NOT NULL,
+ UNIQUE KEY `id_command` (`id`,`command`),
+ KEY `id` (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Table structure for table `aowow_emotes_sounds`
+--
+
+DROP TABLE IF EXISTS `aowow_emotes_sounds`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `aowow_emotes_sounds` (
+ `emoteId` smallint(5) unsigned NOT NULL,
+ `raceId` tinyint(3) unsigned NOT NULL,
+ `gender` tinyint(3) unsigned NOT NULL,
+ `soundId` smallint(5) unsigned NOT NULL,
+ UNIQUE KEY `emoteId_raceId_gender_soundId` (`emoteId`,`raceId`,`gender`,`soundId`),
+ KEY `emoteId` (`emoteId`),
+ KEY `raceId` (`raceId`),
+ KEY `soundId` (`soundId`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Table structure for table `aowow_errors`
+--
+
+DROP TABLE IF EXISTS `aowow_errors`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `aowow_errors` (
+ `date` int(10) unsigned DEFAULT NULL,
+ `version` tinyint(3) unsigned NOT NULL,
+ `phpError` smallint(5) unsigned NOT NULL,
+ `file` varchar(150) NOT NULL,
+ `line` smallint(5) unsigned NOT NULL,
+ `query` varchar(250) NOT NULL,
+ `post` text NOT NULL,
+ `userGroups` smallint(5) unsigned NOT NULL,
+ `message` text DEFAULT NULL,
+ PRIMARY KEY (`file`,`line`,`phpError`,`version`,`userGroups`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Table structure for table `aowow_events`
+--
+
+DROP TABLE IF EXISTS `aowow_events`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `aowow_events` (
+ `id` smallint(5) unsigned NOT NULL,
+ `holidayId` mediumint(8) unsigned NOT NULL DEFAULT 0,
+ `cuFlags` int(10) unsigned NOT NULL DEFAULT 0 COMMENT 'see defines.php for flags',
+ `startTime` int(11) NOT NULL,
+ `endTime` int(11) NOT NULL,
+ `occurence` int(10) unsigned NOT NULL,
+ `length` int(10) unsigned NOT NULL,
+ `requires` varchar(255) DEFAULT NULL,
+ `description` varchar(255) DEFAULT NULL,
+ PRIMARY KEY (`id`),
+ KEY `holidayId` (`holidayId`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Table structure for table `aowow_factions`
+--
+
+DROP TABLE IF EXISTS `aowow_factions`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `aowow_factions` (
+ `id` smallint(5) unsigned NOT NULL,
+ `repIdx` smallint(6) NOT NULL,
+ `baseRepRaceMask1` mediumint(8) unsigned NOT NULL,
+ `baseRepRaceMask2` mediumint(8) unsigned NOT NULL,
+ `baseRepRaceMask3` mediumint(8) unsigned NOT NULL,
+ `baseRepRaceMask4` mediumint(8) unsigned NOT NULL,
+ `baseRepClassMask1` mediumint(8) unsigned NOT NULL,
+ `baseRepClassMask2` mediumint(8) unsigned NOT NULL,
+ `baseRepClassMask3` mediumint(8) unsigned NOT NULL,
+ `baseRepClassMask4` mediumint(8) unsigned NOT NULL,
+ `baseRepValue1` mediumint(9) NOT NULL,
+ `baseRepValue2` mediumint(9) NOT NULL,
+ `baseRepValue3` mediumint(9) NOT NULL,
+ `baseRepValue4` mediumint(9) NOT NULL,
+ `side` tinyint(3) unsigned NOT NULL,
+ `expansion` tinyint(3) unsigned NOT NULL,
+ `qmNpcIds` varchar(12) NOT NULL COMMENT 'space separated',
+ `templateIds` text NOT NULL COMMENT 'space separated',
+ `cuFlags` int(10) unsigned NOT NULL DEFAULT 0 COMMENT 'see defines.php for flags',
+ `parentFactionId` smallint(5) unsigned NOT NULL,
+ `spilloverRateIn` float(8,2) NOT NULL,
+ `spilloverRateOut` float(8,2) NOT NULL,
+ `spilloverMaxRank` tinyint(3) unsigned NOT NULL,
+ `name_loc0` varchar(35) DEFAULT NULL,
+ `name_loc2` varchar(49) DEFAULT NULL,
+ `name_loc3` varchar(40) DEFAULT NULL,
+ `name_loc4` varchar(40) DEFAULT NULL,
+ `name_loc6` varchar(50) DEFAULT NULL,
+ `name_loc8` varchar(47) DEFAULT NULL,
+ PRIMARY KEY (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Table structure for table `aowow_factiontemplate`
+--
+
+DROP TABLE IF EXISTS `aowow_factiontemplate`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `aowow_factiontemplate` (
+ `id` smallint(5) unsigned NOT NULL,
+ `factionId` smallint(5) unsigned NOT NULL,
+ `A` tinyint(4) NOT NULL COMMENT 'Aliance: -1 - hostile, 1 - friendly, 0 - neutral',
+ `H` tinyint(4) NOT NULL COMMENT 'Horde: -1 - hostile, 1 - friendly, 0 - neutral',
+ PRIMARY KEY (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Table structure for table `aowow_glyphproperties`
+--
+
+DROP TABLE IF EXISTS `aowow_glyphproperties`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `aowow_glyphproperties` (
+ `id` smallint(5) unsigned NOT NULL,
+ `spellId` mediumint(8) unsigned NOT NULL,
+ `typeFlags` tinyint(3) unsigned NOT NULL,
+ `iconId` smallint(5) unsigned NOT NULL DEFAULT 0,
+ `iconIdBak` smallint(5) unsigned NOT NULL DEFAULT 0,
+ PRIMARY KEY (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Table structure for table `aowow_guides`
+--
+
+DROP TABLE IF EXISTS `aowow_guides`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `aowow_guides` (
+ `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT,
+ `category` smallint(5) unsigned NOT NULL DEFAULT 0,
+ `classId` tinyint(3) unsigned DEFAULT NULL,
+ `specId` tinyint(4) DEFAULT NULL,
+ `title` varchar(100) NOT NULL DEFAULT '' COMMENT 'title for menus + lists',
+ `name` varchar(100) NOT NULL DEFAULT '' COMMENT 'title for the page tiself',
+ `description` varchar(200) NOT NULL DEFAULT '',
+ `url` varchar(50) DEFAULT NULL,
+ `locale` tinyint(3) unsigned NOT NULL DEFAULT 0,
+ `status` tinyint(3) unsigned NOT NULL DEFAULT 1,
+ `rev` tinyint(3) unsigned NOT NULL DEFAULT 0,
+ `cuFlags` int(10) unsigned NOT NULL DEFAULT 0 COMMENT 'see defines.php for flags',
+ `roles` smallint(5) unsigned NOT NULL DEFAULT 0,
+ `views` mediumint(8) unsigned NOT NULL DEFAULT 0,
+ `userId` mediumint(8) unsigned DEFAULT NULL,
+ `date` int(10) unsigned NOT NULL DEFAULT 0,
+ `approveUserId` mediumint(8) unsigned DEFAULT NULL,
+ `approveDate` int(10) unsigned NOT NULL DEFAULT 0,
+ `deleteUserId` mediumint(8) unsigned DEFAULT NULL,
+ `deleteData` int(10) unsigned NOT NULL DEFAULT 0,
+ PRIMARY KEY (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Table structure for table `aowow_guides_changelog`
+--
+
+DROP TABLE IF EXISTS `aowow_guides_changelog`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `aowow_guides_changelog` (
+ `id` mediumint(8) unsigned NOT NULL,
+ `rev` tinyint(3) unsigned DEFAULT NULL,
+ `date` int(10) unsigned NOT NULL,
+ `userId` mediumint(8) unsigned NOT NULL,
+ `status` tinyint(3) unsigned NOT NULL DEFAULT 0,
+ `msg` varchar(200) DEFAULT '',
+ KEY `id` (`id`) USING BTREE
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Table structure for table `aowow_holidays`
+--
+
+DROP TABLE IF EXISTS `aowow_holidays`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `aowow_holidays` (
+ `id` smallint(5) unsigned NOT NULL,
+ `bossCreature` mediumint(8) unsigned NOT NULL DEFAULT 0,
+ `achievementCatOrId` mediumint(9) NOT NULL DEFAULT 0,
+ `name_loc0` varchar(36) DEFAULT NULL,
+ `name_loc2` varchar(42) DEFAULT NULL,
+ `name_loc3` varchar(36) DEFAULT NULL,
+ `name_loc4` varchar(36) DEFAULT NULL,
+ `name_loc6` varchar(49) DEFAULT NULL,
+ `name_loc8` varchar(29) DEFAULT NULL,
+ `description_loc0` text DEFAULT NULL,
+ `description_loc2` text DEFAULT NULL,
+ `description_loc3` text DEFAULT NULL,
+ `description_loc4` text DEFAULT NULL,
+ `description_loc6` text DEFAULT NULL,
+ `description_loc8` text DEFAULT NULL,
+ `looping` tinyint(4) NOT NULL,
+ `scheduleType` tinyint(4) NOT NULL,
+ `textureString` varchar(30) NOT NULL DEFAULT '',
+ `iconId` smallint(5) unsigned NOT NULL DEFAULT 0,
+ PRIMARY KEY (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Table structure for table `aowow_home_featuredbox`
+--
+
+DROP TABLE IF EXISTS `aowow_home_featuredbox`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `aowow_home_featuredbox` (
+ `id` smallint(5) unsigned NOT NULL,
+ `editorId` int(10) unsigned DEFAULT NULL,
+ `editDate` int(10) unsigned NOT NULL,
+ `startDate` int(10) unsigned NOT NULL DEFAULT 0,
+ `endDate` int(10) unsigned NOT NULL DEFAULT 0,
+ `extraWide` tinyint(3) unsigned NOT NULL DEFAULT 0,
+ `boxBG` varchar(150) DEFAULT NULL,
+ `altHomeLogo` varchar(150) DEFAULT NULL,
+ `altHeaderLogo` varchar(150) DEFAULT NULL,
+ `text_loc0` text DEFAULT NULL,
+ `text_loc2` text DEFAULT NULL,
+ `text_loc3` text DEFAULT NULL,
+ `text_loc4` text DEFAULT NULL,
+ `text_loc6` text DEFAULT NULL,
+ `text_loc8` text DEFAULT NULL,
+ PRIMARY KEY (`id`),
+ KEY `FK_acc_hFBox` (`editorId`),
+ CONSTRAINT `FK_acc_hFBox` FOREIGN KEY (`editorId`) REFERENCES `aowow_account` (`id`) ON DELETE SET NULL ON UPDATE CASCADE
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Table structure for table `aowow_home_featuredbox_overlay`
+--
+
+DROP TABLE IF EXISTS `aowow_home_featuredbox_overlay`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `aowow_home_featuredbox_overlay` (
+ `featureId` smallint(5) unsigned NOT NULL,
+ `left` smallint(5) unsigned NOT NULL,
+ `width` smallint(5) unsigned NOT NULL,
+ `url` varchar(150) NOT NULL,
+ `title_loc0` varchar(100) DEFAULT '',
+ `title_loc2` varchar(100) DEFAULT '',
+ `title_loc3` varchar(100) DEFAULT '',
+ `title_loc4` varchar(100) DEFAULT '',
+ `title_loc6` varchar(100) DEFAULT '',
+ `title_loc8` varchar(100) DEFAULT '',
+ KEY `FK_home_featurebox` (`featureId`),
+ CONSTRAINT `FK_home_featurebox` FOREIGN KEY (`featureId`) REFERENCES `aowow_home_featuredbox` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Table structure for table `aowow_home_oneliner`
+--
+
+DROP TABLE IF EXISTS `aowow_home_oneliner`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `aowow_home_oneliner` (
+ `id` smallint(5) unsigned NOT NULL,
+ `editorId` int(10) unsigned DEFAULT NULL,
+ `editDate` int(10) unsigned NOT NULL,
+ `active` tinyint(3) unsigned NOT NULL,
+ `text_loc0` varchar(200) DEFAULT NULL,
+ `text_loc2` varchar(200) DEFAULT NULL,
+ `text_loc3` varchar(200) DEFAULT NULL,
+ `text_loc4` varchar(200) DEFAULT NULL,
+ `text_loc6` varchar(200) DEFAULT NULL,
+ `text_loc8` varchar(200) DEFAULT NULL,
+ PRIMARY KEY (`id`),
+ KEY `FK_acc_hOneliner` (`editorId`),
+ CONSTRAINT `FK_acc_hOneliner` FOREIGN KEY (`editorId`) REFERENCES `aowow_account` (`id`) ON DELETE SET NULL ON UPDATE CASCADE
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Table structure for table `aowow_home_titles`
+--
+
+DROP TABLE IF EXISTS `aowow_home_titles`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `aowow_home_titles` (
+ `id` smallint(5) unsigned NOT NULL AUTO_INCREMENT,
+ `editorId` int(10) unsigned DEFAULT NULL,
+ `editDate` int(10) unsigned NOT NULL,
+ `active` tinyint(3) unsigned NOT NULL,
+ `locale` tinyint(3) unsigned NOT NULL,
+ `title` varchar(100) NOT NULL,
+ PRIMARY KEY (`id`),
+ UNIQUE KEY `locale_title` (`locale`,`title`),
+ KEY `FK_acc_hTitles` (`editorId`),
+ CONSTRAINT `FK_acc_hTitles` FOREIGN KEY (`editorId`) REFERENCES `aowow_account` (`id`) ON DELETE SET NULL ON UPDATE CASCADE
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Table structure for table `aowow_icons`
+--
+
+DROP TABLE IF EXISTS `aowow_icons`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `aowow_icons` (
+ `id` smallint(5) unsigned NOT NULL AUTO_INCREMENT,
+ `cuFlags` int(10) unsigned NOT NULL DEFAULT 0 COMMENT 'see defines.php for flags',
+ `name` varchar(55) NOT NULL DEFAULT '',
+ `name_source` varchar(55) NOT NULL DEFAULT '',
+ PRIMARY KEY (`id`),
+ KEY `name` (`name`),
+ KEY `idx_sourcename` (`name_source`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Table structure for table `aowow_item_stats`
+--
+
+DROP TABLE IF EXISTS `aowow_item_stats`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `aowow_item_stats` (
+ `type` smallint(5) unsigned NOT NULL,
+ `typeId` mediumint(8) NOT NULL,
+ `nsockets` tinyint(3) unsigned NOT NULL DEFAULT 0,
+ `dps` float(8,2) DEFAULT NULL,
+ `damagetype` tinyint(4) DEFAULT NULL,
+ `dmgmin1` mediumint(5) unsigned DEFAULT NULL,
+ `dmgmax1` mediumint(5) unsigned DEFAULT NULL,
+ `speed` float(8,2) DEFAULT NULL,
+ `mledps` float(8,2) DEFAULT NULL,
+ `mledmgmin` mediumint(5) unsigned DEFAULT NULL,
+ `mledmgmax` mediumint(5) unsigned DEFAULT NULL,
+ `mlespeed` float(8,2) DEFAULT NULL,
+ `rgddps` float(8,2) DEFAULT NULL,
+ `rgddmgmin` mediumint(5) unsigned DEFAULT NULL,
+ `rgddmgmax` mediumint(5) unsigned DEFAULT NULL,
+ `rgdspeed` float(8,2) DEFAULT NULL,
+ `dmg` float(8,2) NOT NULL DEFAULT 0.00,
+ `mana` mediumint(6) NOT NULL DEFAULT 0,
+ `health` mediumint(6) NOT NULL DEFAULT 0,
+ `agi` mediumint(6) NOT NULL DEFAULT 0,
+ `str` mediumint(6) NOT NULL DEFAULT 0,
+ `int` mediumint(6) NOT NULL DEFAULT 0,
+ `spi` mediumint(6) NOT NULL DEFAULT 0,
+ `sta` mediumint(6) NOT NULL DEFAULT 0,
+ `energy` mediumint(6) NOT NULL DEFAULT 0,
+ `rage` mediumint(6) NOT NULL DEFAULT 0,
+ `focus` mediumint(6) NOT NULL DEFAULT 0,
+ `runic` mediumint(6) NOT NULL DEFAULT 0,
+ `defrtng` mediumint(6) NOT NULL DEFAULT 0,
+ `dodgertng` mediumint(6) NOT NULL DEFAULT 0,
+ `parryrtng` mediumint(6) NOT NULL DEFAULT 0,
+ `blockrtng` mediumint(6) NOT NULL DEFAULT 0,
+ `mlehitrtng` mediumint(6) NOT NULL DEFAULT 0,
+ `rgdhitrtng` mediumint(6) NOT NULL DEFAULT 0,
+ `splhitrtng` mediumint(6) NOT NULL DEFAULT 0,
+ `mlecritstrkrtng` mediumint(6) NOT NULL DEFAULT 0,
+ `rgdcritstrkrtng` mediumint(6) NOT NULL DEFAULT 0,
+ `splcritstrkrtng` mediumint(6) NOT NULL DEFAULT 0,
+ `_mlehitrtng` mediumint(6) NOT NULL DEFAULT 0,
+ `_rgdhitrtng` mediumint(6) NOT NULL DEFAULT 0,
+ `_splhitrtng` mediumint(6) NOT NULL DEFAULT 0,
+ `_mlecritstrkrtng` mediumint(6) NOT NULL DEFAULT 0,
+ `_rgdcritstrkrtng` mediumint(6) NOT NULL DEFAULT 0,
+ `_splcritstrkrtng` mediumint(6) NOT NULL DEFAULT 0,
+ `mlehastertng` mediumint(6) NOT NULL DEFAULT 0,
+ `rgdhastertng` mediumint(6) NOT NULL DEFAULT 0,
+ `splhastertng` mediumint(6) NOT NULL DEFAULT 0,
+ `hitrtng` mediumint(6) NOT NULL DEFAULT 0,
+ `critstrkrtng` mediumint(6) NOT NULL DEFAULT 0,
+ `_hitrtng` mediumint(6) NOT NULL DEFAULT 0,
+ `_critstrkrtng` mediumint(6) NOT NULL DEFAULT 0,
+ `resirtng` mediumint(6) NOT NULL DEFAULT 0,
+ `hastertng` mediumint(6) NOT NULL DEFAULT 0,
+ `exprtng` mediumint(6) NOT NULL DEFAULT 0,
+ `atkpwr` mediumint(6) NOT NULL DEFAULT 0,
+ `mleatkpwr` mediumint(6) NOT NULL DEFAULT 0,
+ `rgdatkpwr` mediumint(6) NOT NULL DEFAULT 0,
+ `feratkpwr` mediumint(6) NOT NULL DEFAULT 0,
+ `splheal` mediumint(6) NOT NULL DEFAULT 0,
+ `spldmg` mediumint(6) NOT NULL DEFAULT 0,
+ `manargn` mediumint(6) NOT NULL DEFAULT 0,
+ `armorpenrtng` mediumint(6) NOT NULL DEFAULT 0,
+ `splpwr` mediumint(6) NOT NULL DEFAULT 0,
+ `healthrgn` mediumint(6) NOT NULL DEFAULT 0,
+ `splpen` mediumint(6) NOT NULL DEFAULT 0,
+ `block` mediumint(6) NOT NULL DEFAULT 0,
+ `mastrtng` mediumint(6) NOT NULL DEFAULT 0,
+ `armor` mediumint(6) NOT NULL DEFAULT 0,
+ `armorbonus` mediumint(6) DEFAULT NULL,
+ `firres` mediumint(6) NOT NULL DEFAULT 0,
+ `frores` mediumint(6) NOT NULL DEFAULT 0,
+ `holres` mediumint(6) NOT NULL DEFAULT 0,
+ `shares` mediumint(6) NOT NULL DEFAULT 0,
+ `natres` mediumint(6) NOT NULL DEFAULT 0,
+ `arcres` mediumint(6) NOT NULL DEFAULT 0,
+ `firsplpwr` mediumint(6) NOT NULL DEFAULT 0,
+ `frosplpwr` mediumint(6) NOT NULL DEFAULT 0,
+ `holsplpwr` mediumint(6) NOT NULL DEFAULT 0,
+ `shasplpwr` mediumint(6) NOT NULL DEFAULT 0,
+ `natsplpwr` mediumint(6) NOT NULL DEFAULT 0,
+ `arcsplpwr` mediumint(6) NOT NULL DEFAULT 0,
+ PRIMARY KEY (`type`,`typeId`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Table structure for table `aowow_itemenchantment`
+--
+
+DROP TABLE IF EXISTS `aowow_itemenchantment`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `aowow_itemenchantment` (
+ `id` smallint(5) unsigned NOT NULL,
+ `charges` tinyint(3) unsigned NOT NULL,
+ `cuFlags` int(10) unsigned NOT NULL DEFAULT 0 COMMENT 'see defines.php for flags',
+ `procChance` tinyint(3) unsigned NOT NULL,
+ `ppmRate` float NOT NULL,
+ `type1` tinyint(3) unsigned NOT NULL,
+ `type2` tinyint(3) unsigned NOT NULL,
+ `type3` tinyint(3) unsigned NOT NULL,
+ `amount1` smallint(6) NOT NULL,
+ `amount2` smallint(6) NOT NULL,
+ `amount3` smallint(6) NOT NULL,
+ `object1` mediumint(8) unsigned NOT NULL,
+ `object2` mediumint(8) unsigned NOT NULL,
+ `object3` smallint(5) unsigned NOT NULL,
+ `name_loc0` varchar(65) DEFAULT NULL,
+ `name_loc2` varchar(91) DEFAULT NULL,
+ `name_loc3` varchar(84) DEFAULT NULL,
+ `name_loc4` varchar(84) DEFAULT NULL,
+ `name_loc6` varchar(89) DEFAULT NULL,
+ `name_loc8` varchar(96) DEFAULT NULL,
+ `conditionId` tinyint(3) unsigned NOT NULL,
+ `skillLine` smallint(5) unsigned NOT NULL,
+ `skillLevel` smallint(5) unsigned NOT NULL,
+ `requiredLevel` tinyint(3) unsigned NOT NULL,
+ PRIMARY KEY (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Table structure for table `aowow_itemrandomenchant`
+--
+
+DROP TABLE IF EXISTS `aowow_itemrandomenchant`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `aowow_itemrandomenchant` (
+ `id` smallint(6) NOT NULL,
+ `name_loc0` varchar(250) DEFAULT NULL,
+ `name_loc2` varchar(250) DEFAULT NULL,
+ `name_loc3` varchar(250) DEFAULT NULL,
+ `name_loc4` varchar(250) DEFAULT NULL,
+ `name_loc6` varchar(250) DEFAULT NULL,
+ `name_loc8` varchar(250) DEFAULT NULL,
+ `nameINT` char(250) NOT NULL,
+ `enchantId1` smallint(5) unsigned NOT NULL,
+ `enchantId2` smallint(5) unsigned NOT NULL,
+ `enchantId3` smallint(5) unsigned NOT NULL,
+ `enchantId4` smallint(5) unsigned NOT NULL,
+ `enchantId5` smallint(5) unsigned NOT NULL,
+ `allocationPct1` smallint(5) unsigned NOT NULL,
+ `allocationPct2` smallint(5) unsigned NOT NULL,
+ `allocationPct3` smallint(5) unsigned NOT NULL,
+ `allocationPct4` smallint(5) unsigned NOT NULL,
+ `allocationPct5` smallint(5) unsigned NOT NULL,
+ PRIMARY KEY (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Table structure for table `aowow_items`
+--
+
+DROP TABLE IF EXISTS `aowow_items`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `aowow_items` (
+ `id` mediumint(8) unsigned NOT NULL DEFAULT 0,
+ `class` tinyint(3) unsigned NOT NULL DEFAULT 0,
+ `classBak` tinyint(4) NOT NULL,
+ `subClass` tinyint(4) NOT NULL DEFAULT 0,
+ `subClassBak` tinyint(4) NOT NULL,
+ `soundOverrideSubclass` tinyint(4) NOT NULL,
+ `subSubClass` tinyint(4) NOT NULL,
+ `name_loc0` varchar(127) DEFAULT NULL,
+ `name_loc2` varchar(127) DEFAULT NULL,
+ `name_loc3` varchar(127) DEFAULT NULL,
+ `name_loc4` varchar(127) DEFAULT NULL,
+ `name_loc6` varchar(127) DEFAULT NULL,
+ `name_loc8` varchar(127) DEFAULT NULL,
+ `iconId` smallint(5) unsigned NOT NULL DEFAULT 0,
+ `displayId` mediumint(8) unsigned NOT NULL DEFAULT 0,
+ `spellVisualId` smallint(5) unsigned NOT NULL DEFAULT 0,
+ `quality` tinyint(3) unsigned NOT NULL DEFAULT 0,
+ `flags` int(10) unsigned NOT NULL DEFAULT 0,
+ `flagsExtra` int(10) unsigned NOT NULL DEFAULT 0,
+ `buyCount` tinyint(3) unsigned NOT NULL DEFAULT 1,
+ `buyPrice` int(11) NOT NULL DEFAULT 0,
+ `sellPrice` int(10) unsigned NOT NULL DEFAULT 0,
+ `repairPrice` int(10) unsigned NOT NULL,
+ `slot` tinyint(4) NOT NULL,
+ `slotBak` tinyint(3) unsigned NOT NULL DEFAULT 0,
+ `requiredClass` smallint(5) unsigned NOT NULL DEFAULT 0,
+ `requiredRace` smallint(5) unsigned NOT NULL DEFAULT 0,
+ `itemLevel` smallint(5) unsigned NOT NULL DEFAULT 0,
+ `requiredLevel` tinyint(3) unsigned NOT NULL DEFAULT 0,
+ `requiredSkill` smallint(5) unsigned NOT NULL DEFAULT 0,
+ `requiredSkillRank` smallint(5) unsigned NOT NULL DEFAULT 0,
+ `requiredSpell` mediumint(8) unsigned NOT NULL DEFAULT 0,
+ `requiredHonorRank` mediumint(8) unsigned NOT NULL DEFAULT 0,
+ `requiredCityRank` mediumint(8) unsigned NOT NULL DEFAULT 0,
+ `requiredFaction` smallint(5) unsigned NOT NULL DEFAULT 0,
+ `requiredFactionRank` smallint(5) unsigned NOT NULL DEFAULT 0,
+ `maxCount` int(11) NOT NULL DEFAULT 0,
+ `cuFlags` int(10) unsigned NOT NULL DEFAULT 0 COMMENT 'see defines.php for flags',
+ `model` varchar(50) NOT NULL,
+ `stackable` int(11) DEFAULT 1,
+ `slots` tinyint(3) unsigned NOT NULL DEFAULT 0,
+ `statType1` tinyint(3) unsigned NOT NULL DEFAULT 0,
+ `statValue1` smallint(6) NOT NULL DEFAULT 0,
+ `statType2` tinyint(3) unsigned NOT NULL DEFAULT 0,
+ `statValue2` smallint(6) NOT NULL DEFAULT 0,
+ `statType3` tinyint(3) unsigned NOT NULL DEFAULT 0,
+ `statValue3` smallint(6) NOT NULL DEFAULT 0,
+ `statType4` tinyint(3) unsigned NOT NULL DEFAULT 0,
+ `statValue4` smallint(6) NOT NULL DEFAULT 0,
+ `statType5` tinyint(3) unsigned NOT NULL DEFAULT 0,
+ `statValue5` smallint(6) NOT NULL DEFAULT 0,
+ `statType6` tinyint(3) unsigned NOT NULL DEFAULT 0,
+ `statValue6` smallint(6) NOT NULL DEFAULT 0,
+ `statType7` tinyint(3) unsigned NOT NULL DEFAULT 0,
+ `statValue7` smallint(6) NOT NULL DEFAULT 0,
+ `statType8` tinyint(3) unsigned NOT NULL DEFAULT 0,
+ `statValue8` smallint(6) NOT NULL DEFAULT 0,
+ `statType9` tinyint(3) unsigned NOT NULL DEFAULT 0,
+ `statValue9` smallint(6) NOT NULL DEFAULT 0,
+ `statType10` tinyint(3) unsigned NOT NULL DEFAULT 0,
+ `statValue10` smallint(6) NOT NULL DEFAULT 0,
+ `scalingStatDistribution` smallint(6) NOT NULL DEFAULT 0,
+ `scalingStatValue` int(10) unsigned NOT NULL DEFAULT 0,
+ `dmgMin1` float NOT NULL DEFAULT 0,
+ `dmgMax1` float NOT NULL DEFAULT 0,
+ `dmgType1` tinyint(3) unsigned NOT NULL DEFAULT 0,
+ `dmgMin2` float NOT NULL DEFAULT 0,
+ `dmgMax2` float NOT NULL DEFAULT 0,
+ `dmgType2` tinyint(3) unsigned NOT NULL DEFAULT 0,
+ `delay` smallint(5) unsigned NOT NULL DEFAULT 1000,
+ `armor` smallint(5) unsigned NOT NULL DEFAULT 0,
+ `armorDamageModifier` float NOT NULL DEFAULT 0,
+ `block` mediumint(8) unsigned NOT NULL DEFAULT 0,
+ `resHoly` tinyint(3) unsigned NOT NULL DEFAULT 0,
+ `resFire` tinyint(3) unsigned NOT NULL DEFAULT 0,
+ `resNature` tinyint(3) unsigned NOT NULL DEFAULT 0,
+ `resFrost` tinyint(3) unsigned NOT NULL DEFAULT 0,
+ `resShadow` tinyint(3) unsigned NOT NULL DEFAULT 0,
+ `resArcane` tinyint(3) unsigned NOT NULL DEFAULT 0,
+ `ammoType` tinyint(3) unsigned NOT NULL DEFAULT 0,
+ `rangedModRange` float NOT NULL DEFAULT 0,
+ `spellId1` mediumint(9) NOT NULL DEFAULT 0,
+ `spellTrigger1` tinyint(3) unsigned NOT NULL DEFAULT 0,
+ `spellCharges1` smallint(6) DEFAULT NULL,
+ `spellppmRate1` float NOT NULL DEFAULT 0,
+ `spellCooldown1` int(11) NOT NULL DEFAULT -1,
+ `spellCategory1` smallint(5) unsigned NOT NULL DEFAULT 0,
+ `spellCategoryCooldown1` int(11) NOT NULL DEFAULT -1,
+ `spellId2` mediumint(9) NOT NULL DEFAULT 0,
+ `spellTrigger2` tinyint(3) unsigned NOT NULL DEFAULT 0,
+ `spellCharges2` smallint(6) DEFAULT NULL,
+ `spellppmRate2` float NOT NULL DEFAULT 0,
+ `spellCooldown2` int(11) NOT NULL DEFAULT -1,
+ `spellCategory2` smallint(5) unsigned NOT NULL DEFAULT 0,
+ `spellCategoryCooldown2` int(11) NOT NULL DEFAULT -1,
+ `spellId3` mediumint(9) NOT NULL DEFAULT 0,
+ `spellTrigger3` tinyint(3) unsigned NOT NULL DEFAULT 0,
+ `spellCharges3` smallint(6) DEFAULT NULL,
+ `spellppmRate3` float NOT NULL DEFAULT 0,
+ `spellCooldown3` int(11) NOT NULL DEFAULT -1,
+ `spellCategory3` smallint(5) unsigned NOT NULL DEFAULT 0,
+ `spellCategoryCooldown3` int(11) NOT NULL DEFAULT -1,
+ `spellId4` mediumint(9) NOT NULL DEFAULT 0,
+ `spellTrigger4` tinyint(3) unsigned NOT NULL DEFAULT 0,
+ `spellCharges4` smallint(6) DEFAULT NULL,
+ `spellppmRate4` float NOT NULL DEFAULT 0,
+ `spellCooldown4` int(11) NOT NULL DEFAULT -1,
+ `spellCategory4` smallint(5) unsigned NOT NULL DEFAULT 0,
+ `spellCategoryCooldown4` int(11) NOT NULL DEFAULT -1,
+ `spellId5` mediumint(9) NOT NULL DEFAULT 0,
+ `spellTrigger5` tinyint(3) unsigned NOT NULL DEFAULT 0,
+ `spellCharges5` smallint(6) DEFAULT NULL,
+ `spellppmRate5` float NOT NULL DEFAULT 0,
+ `spellCooldown5` int(11) NOT NULL DEFAULT -1,
+ `spellCategory5` smallint(5) unsigned NOT NULL DEFAULT 0,
+ `spellCategoryCooldown5` int(11) NOT NULL DEFAULT -1,
+ `bonding` tinyint(3) unsigned NOT NULL DEFAULT 0,
+ `description_loc0` varchar(255) DEFAULT NULL,
+ `description_loc2` varchar(255) DEFAULT NULL,
+ `description_loc3` varchar(255) DEFAULT NULL,
+ `description_loc4` varchar(255) DEFAULT NULL,
+ `description_loc6` varchar(255) DEFAULT NULL,
+ `description_loc8` varchar(255) DEFAULT NULL,
+ `pageTextId` mediumint(8) unsigned NOT NULL DEFAULT 0,
+ `languageId` tinyint(3) unsigned NOT NULL DEFAULT 0,
+ `startQuest` mediumint(8) unsigned NOT NULL DEFAULT 0,
+ `lockId` mediumint(8) unsigned NOT NULL DEFAULT 0,
+ `material` tinyint(4) NOT NULL DEFAULT 0,
+ `randomEnchant` mediumint(9) NOT NULL DEFAULT 0,
+ `itemset` mediumint(8) unsigned NOT NULL DEFAULT 0,
+ `durability` smallint(5) unsigned NOT NULL DEFAULT 0,
+ `area` mediumint(8) unsigned NOT NULL DEFAULT 0,
+ `map` smallint(6) NOT NULL DEFAULT 0,
+ `bagFamily` mediumint(9) NOT NULL DEFAULT 0,
+ `totemCategory` mediumint(9) NOT NULL DEFAULT 0,
+ `socketColor1` tinyint(4) NOT NULL DEFAULT 0,
+ `socketContent1` mediumint(9) NOT NULL DEFAULT 0,
+ `socketColor2` tinyint(4) NOT NULL DEFAULT 0,
+ `socketContent2` mediumint(9) NOT NULL DEFAULT 0,
+ `socketColor3` tinyint(4) NOT NULL DEFAULT 0,
+ `socketContent3` mediumint(9) NOT NULL DEFAULT 0,
+ `socketBonus` mediumint(9) NOT NULL DEFAULT 0,
+ `gemColorMask` mediumint(9) NOT NULL DEFAULT 0,
+ `requiredDisenchantSkill` smallint(6) NOT NULL DEFAULT -1,
+ `disenchantId` mediumint(8) unsigned NOT NULL DEFAULT 0,
+ `duration` int(10) unsigned NOT NULL DEFAULT 0,
+ `itemLimitCategory` smallint(6) NOT NULL DEFAULT 0,
+ `eventId` smallint(5) unsigned NOT NULL,
+ `scriptName` varchar(64) NOT NULL DEFAULT '',
+ `foodType` tinyint(3) unsigned NOT NULL DEFAULT 0,
+ `gemEnchantmentId` mediumint(9) NOT NULL,
+ `minMoneyLoot` int(10) unsigned NOT NULL DEFAULT 0,
+ `maxMoneyLoot` int(10) unsigned NOT NULL DEFAULT 0,
+ `pickUpSoundId` smallint(5) unsigned NOT NULL DEFAULT 0,
+ `dropDownSoundId` smallint(5) unsigned NOT NULL DEFAULT 0,
+ `sheatheSoundId` smallint(5) unsigned NOT NULL DEFAULT 0,
+ `unsheatheSoundId` smallint(5) unsigned NOT NULL DEFAULT 0,
+ `flagsCustom` int(10) unsigned NOT NULL DEFAULT 0,
+ PRIMARY KEY (`id`),
+ KEY `items_index` (`class`),
+ KEY `idx_model` (`displayId`),
+ KEY `idx_faction` (`requiredFaction`),
+ KEY `iconId` (`iconId`),
+ KEY `idx_spell1` (`spellId1`),
+ KEY `idx_spell2` (`spellId2`),
+ KEY `idx_spell3` (`spellId3`),
+ KEY `idx_spell4` (`spellId4`),
+ KEY `idx_spell5` (`spellId5`),
+ KEY `idx_trigger1` (`spellTrigger1`),
+ KEY `idx_trigger2` (`spellTrigger2`),
+ KEY `idx_trigger3` (`spellTrigger3`),
+ KEY `idx_trigger4` (`spellTrigger4`),
+ KEY `idx_trigger5` (`spellTrigger5`),
+ KEY `idx_reqskill` (`requiredSkill`),
+ KEY `idx_name0` (`name_loc0`),
+ KEY `idx_name2` (`name_loc2`),
+ KEY `idx_name3` (`name_loc3`),
+ KEY `idx_name4` (`name_loc4`),
+ KEY `idx_name6` (`name_loc6`),
+ KEY `idx_name8` (`name_loc8`),
+ KEY `idx_itemset` (`itemset`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Table structure for table `aowow_items_search`
+--
+
+DROP TABLE IF EXISTS `aowow_items_search`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `aowow_items_search` (
+ `id` mediumint(8) unsigned NOT NULL,
+ `locale` tinyint(3) unsigned NOT NULL,
+ `nName` varchar(127) DEFAULT NULL,
+ `nDescription` varchar(255) DEFAULT NULL,
+ `nEffects` text DEFAULT NULL,
+ PRIMARY KEY (`id`,`locale`),
+ FULLTEXT KEY `idx_ft_na` (`nName`),
+ FULLTEXT KEY `idx_ft_description` (`nDescription`),
+ FULLTEXT KEY `idx_ft_effects` (`nEffects`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Table structure for table `aowow_items_sounds`
+--
+
+DROP TABLE IF EXISTS `aowow_items_sounds`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `aowow_items_sounds` (
+ `soundId` smallint(5) unsigned NOT NULL,
+ `subClassMask` mediumint(8) unsigned NOT NULL,
+ PRIMARY KEY (`soundId`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='actually .. its only weapon related sounds in here';
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Table structure for table `aowow_itemset`
+--
+
+DROP TABLE IF EXISTS `aowow_itemset`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `aowow_itemset` (
+ `id` int(11) NOT NULL,
+ `refSetId` int(11) NOT NULL,
+ `cuFlags` int(10) unsigned NOT NULL DEFAULT 0 COMMENT 'see defines.php for flags',
+ `name_loc0` varchar(255) DEFAULT NULL,
+ `name_loc2` varchar(255) DEFAULT NULL,
+ `name_loc3` varchar(255) DEFAULT NULL,
+ `name_loc4` varchar(255) DEFAULT NULL,
+ `name_loc6` varchar(255) DEFAULT NULL,
+ `name_loc8` varchar(255) DEFAULT NULL,
+ `item1` mediumint(8) unsigned NOT NULL DEFAULT 0,
+ `item2` mediumint(8) unsigned NOT NULL DEFAULT 0,
+ `item3` mediumint(8) unsigned NOT NULL DEFAULT 0,
+ `item4` mediumint(8) unsigned NOT NULL DEFAULT 0,
+ `item5` mediumint(8) unsigned NOT NULL DEFAULT 0,
+ `item6` mediumint(8) unsigned NOT NULL DEFAULT 0,
+ `item7` mediumint(8) unsigned NOT NULL DEFAULT 0,
+ `item8` mediumint(8) unsigned NOT NULL DEFAULT 0,
+ `item9` mediumint(8) unsigned NOT NULL DEFAULT 0,
+ `item10` mediumint(8) unsigned NOT NULL DEFAULT 0,
+ `spell1` mediumint(8) unsigned NOT NULL DEFAULT 0,
+ `spell2` mediumint(8) unsigned NOT NULL DEFAULT 0,
+ `spell3` mediumint(8) unsigned NOT NULL DEFAULT 0,
+ `spell4` mediumint(8) unsigned NOT NULL DEFAULT 0,
+ `spell5` mediumint(8) unsigned NOT NULL DEFAULT 0,
+ `spell6` mediumint(8) unsigned NOT NULL DEFAULT 0,
+ `spell7` mediumint(8) unsigned NOT NULL DEFAULT 0,
+ `spell8` mediumint(8) unsigned NOT NULL DEFAULT 0,
+ `bonus1` tinyint(3) unsigned NOT NULL DEFAULT 0,
+ `bonus2` tinyint(3) unsigned NOT NULL DEFAULT 0,
+ `bonus3` tinyint(3) unsigned NOT NULL DEFAULT 0,
+ `bonus4` tinyint(3) unsigned NOT NULL DEFAULT 0,
+ `bonus5` tinyint(3) unsigned NOT NULL DEFAULT 0,
+ `bonus6` tinyint(3) unsigned NOT NULL DEFAULT 0,
+ `bonus7` tinyint(3) unsigned NOT NULL DEFAULT 0,
+ `bonus8` tinyint(3) unsigned NOT NULL DEFAULT 0,
+ `bonusText_loc0` text DEFAULT NULL,
+ `bonusText_loc2` text DEFAULT NULL,
+ `bonusText_loc3` text DEFAULT NULL,
+ `bonusText_loc4` text DEFAULT NULL,
+ `bonusText_loc6` text DEFAULT NULL,
+ `bonusText_loc8` text DEFAULT NULL,
+ `npieces` tinyint(4) NOT NULL DEFAULT 0,
+ `minLevel` smallint(6) NOT NULL DEFAULT 0,
+ `maxLevel` smallint(6) NOT NULL DEFAULT 0,
+ `reqLevel` smallint(6) NOT NULL DEFAULT 0,
+ `classMask` mediumint(9) NOT NULL DEFAULT 0,
+ `heroic` tinyint(4) NOT NULL DEFAULT 0 COMMENT 'bool',
+ `quality` tinyint(4) NOT NULL DEFAULT 0,
+ `type` smallint(6) NOT NULL DEFAULT 0 COMMENT 'g_itemset_types',
+ `contentGroup` smallint(6) NOT NULL DEFAULT 0 COMMENT 'g_itemset_notes',
+ `eventId` smallint(5) unsigned NOT NULL DEFAULT 0,
+ `skillId` smallint(5) unsigned NOT NULL DEFAULT 0,
+ `skillLevel` smallint(5) unsigned NOT NULL DEFAULT 0,
+ PRIMARY KEY (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Table structure for table `aowow_loot_link`
+--
+
+DROP TABLE IF EXISTS `aowow_loot_link`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `aowow_loot_link` (
+ `npcId` mediumint(8) unsigned NOT NULL,
+ `objectId` mediumint(8) unsigned NOT NULL,
+ `difficulty` tinyint(3) unsigned NOT NULL DEFAULT 1,
+ `priority` tinyint(3) unsigned NOT NULL COMMENT '1: use this npc from group encounter (others 0)',
+ `encounterId` mediumint(8) unsigned NOT NULL COMMENT 'as title reference',
+ UNIQUE KEY `npcId_difficulty` (`npcId`,`difficulty`),
+ KEY `objectId` (`objectId`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Table structure for table `aowow_mails`
+--
+
+DROP TABLE IF EXISTS `aowow_mails`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `aowow_mails` (
+ `id` smallint(6) NOT NULL,
+ `cuFlags` int(10) unsigned NOT NULL DEFAULT 0 COMMENT 'see defines.php for flags',
+ `subject_loc0` varchar(128) DEFAULT NULL,
+ `subject_loc2` varchar(128) DEFAULT NULL,
+ `subject_loc3` varchar(128) DEFAULT NULL,
+ `subject_loc4` varchar(128) DEFAULT NULL,
+ `subject_loc6` varchar(128) DEFAULT NULL,
+ `subject_loc8` varchar(128) DEFAULT NULL,
+ `text_loc0` text DEFAULT NULL,
+ `text_loc2` text DEFAULT NULL,
+ `text_loc3` text DEFAULT NULL,
+ `text_loc4` text DEFAULT NULL,
+ `text_loc6` text DEFAULT NULL,
+ `text_loc8` text DEFAULT NULL,
+ `attachment` smallint(5) unsigned NOT NULL DEFAULT 0,
+ PRIMARY KEY (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Table structure for table `aowow_objectdifficulty`
+--
+
+DROP TABLE IF EXISTS `aowow_objectdifficulty`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `aowow_objectdifficulty` (
+ `normal10` mediumint(8) unsigned NOT NULL,
+ `normal25` mediumint(8) unsigned NOT NULL,
+ `heroic10` mediumint(8) unsigned NOT NULL,
+ `heroic25` mediumint(8) unsigned NOT NULL,
+ `mapType` tinyint(3) unsigned NOT NULL,
+ KEY `normal10` (`normal10`),
+ KEY `normal25` (`normal25`),
+ KEY `heroic10` (`heroic10`),
+ KEY `heroic25` (`heroic25`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Table structure for table `aowow_objects`
+--
+
+DROP TABLE IF EXISTS `aowow_objects`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `aowow_objects` (
+ `id` mediumint(8) unsigned NOT NULL DEFAULT 0,
+ `type` tinyint(3) unsigned NOT NULL DEFAULT 0,
+ `typeCat` tinyint(4) NOT NULL DEFAULT 0,
+ `event` smallint(5) unsigned NOT NULL DEFAULT 0,
+ `displayId` mediumint(8) unsigned NOT NULL DEFAULT 0,
+ `name_loc0` varchar(100) DEFAULT NULL,
+ `name_loc2` varchar(100) DEFAULT NULL,
+ `name_loc3` varchar(100) DEFAULT NULL,
+ `name_loc4` varchar(100) DEFAULT NULL,
+ `name_loc6` varchar(100) DEFAULT NULL,
+ `name_loc8` varchar(100) DEFAULT NULL,
+ `faction` smallint(5) unsigned NOT NULL DEFAULT 0,
+ `flags` int(10) unsigned NOT NULL DEFAULT 0,
+ `cuFlags` int(10) unsigned NOT NULL DEFAULT 0 COMMENT 'see defines.php for flags',
+ `lootId` mediumint(8) unsigned NOT NULL DEFAULT 0,
+ `lockId` smallint(5) unsigned NOT NULL DEFAULT 0,
+ `reqSkill` smallint(5) unsigned NOT NULL DEFAULT 0,
+ `pageTextId` smallint(5) unsigned NOT NULL DEFAULT 0,
+ `linkedTrap` mediumint(8) unsigned NOT NULL DEFAULT 0,
+ `reqQuest` mediumint(9) NOT NULL DEFAULT 0,
+ `spellFocusId` smallint(5) unsigned NOT NULL DEFAULT 0,
+ `onUseSpell` mediumint(8) unsigned NOT NULL DEFAULT 0,
+ `onSuccessSpell` mediumint(8) unsigned NOT NULL DEFAULT 0,
+ `auraSpell` mediumint(8) unsigned NOT NULL DEFAULT 0,
+ `triggeredSpell` mediumint(8) unsigned NOT NULL DEFAULT 0,
+ `miscInfo` varchar(128) NOT NULL,
+ `ScriptOrAI` varchar(64) DEFAULT NULL,
+ `StringId` varchar(64) DEFAULT NULL,
+ PRIMARY KEY (`id`),
+ KEY `idx_onusespell` (`onUseSpell`),
+ KEY `idx_onsuccessspell` (`onSuccessSpell`),
+ KEY `idx_auraspell` (`auraSpell`),
+ KEY `idx_triggeredspell` (`triggeredSpell`),
+ KEY `idx_name0` (`name_loc0`),
+ KEY `idx_name2` (`name_loc2`),
+ KEY `idx_name3` (`name_loc3`),
+ KEY `idx_name4` (`name_loc4`),
+ KEY `idx_name6` (`name_loc6`),
+ KEY `idx_name8` (`name_loc8`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Table structure for table `aowow_objects_search`
+--
+
+DROP TABLE IF EXISTS `aowow_objects_search`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `aowow_objects_search` (
+ `id` mediumint(8) unsigned NOT NULL,
+ `locale` tinyint(3) unsigned NOT NULL,
+ `nName` varchar(127) DEFAULT NULL,
+ PRIMARY KEY (`id`,`locale`),
+ FULLTEXT KEY `idx_ft_na` (`nName`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Table structure for table `aowow_pet`
+--
+
+DROP TABLE IF EXISTS `aowow_pet`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `aowow_pet` (
+ `id` int(11) NOT NULL,
+ `category` mediumint(9) NOT NULL,
+ `cuFlags` int(10) unsigned NOT NULL DEFAULT 0 COMMENT 'see defines.php for flags',
+ `minLevel` smallint(6) NOT NULL,
+ `maxLevel` smallint(6) NOT NULL,
+ `foodMask` int(11) NOT NULL,
+ `type` tinyint(4) NOT NULL,
+ `exotic` tinyint(4) NOT NULL,
+ `expansion` tinyint(4) NOT NULL,
+ `name_loc0` varchar(64) DEFAULT NULL,
+ `name_loc2` varchar(64) DEFAULT NULL,
+ `name_loc3` varchar(64) DEFAULT NULL,
+ `name_loc4` varchar(64) DEFAULT NULL,
+ `name_loc6` varchar(64) DEFAULT NULL,
+ `name_loc8` varchar(64) DEFAULT NULL,
+ `iconId` smallint(5) unsigned NOT NULL DEFAULT 0,
+ `skillLineId` mediumint(9) NOT NULL,
+ `spellId1` mediumint(9) NOT NULL,
+ `spellId2` mediumint(9) NOT NULL,
+ `spellId3` mediumint(9) NOT NULL,
+ `spellId4` mediumint(9) NOT NULL,
+ `armor` mediumint(9) NOT NULL,
+ `damage` mediumint(9) NOT NULL,
+ `health` mediumint(9) NOT NULL,
+ PRIMARY KEY (`id`),
+ KEY `iconId` (`iconId`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Table structure for table `aowow_profiler_arena_team`
+--
+
+DROP TABLE IF EXISTS `aowow_profiler_arena_team`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `aowow_profiler_arena_team` (
+ `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
+ `realm` tinyint(3) unsigned NOT NULL,
+ `realmGUID` int(10) unsigned NOT NULL,
+ `name` varchar(24) NOT NULL,
+ `nameUrl` varchar(24) NOT NULL,
+ `type` tinyint(3) unsigned NOT NULL DEFAULT 0,
+ `cuFlags` int(10) unsigned NOT NULL DEFAULT 0 COMMENT 'see defines.php for flags',
+ `stub` tinyint(1) DEFAULT 0 COMMENT 'arena team stub needs resync',
+ `rating` smallint(5) unsigned NOT NULL DEFAULT 0,
+ `seasonGames` smallint(5) unsigned NOT NULL DEFAULT 0,
+ `seasonWins` smallint(5) unsigned NOT NULL DEFAULT 0,
+ `weekGames` smallint(5) unsigned NOT NULL DEFAULT 0,
+ `weekWins` smallint(5) unsigned NOT NULL DEFAULT 0,
+ `rank` int(10) unsigned NOT NULL DEFAULT 0,
+ `backgroundColor` int(10) unsigned NOT NULL DEFAULT 0,
+ `emblemStyle` tinyint(3) unsigned NOT NULL DEFAULT 0,
+ `emblemColor` int(10) unsigned NOT NULL DEFAULT 0,
+ `borderStyle` tinyint(3) unsigned NOT NULL DEFAULT 0,
+ `borderColor` int(10) unsigned NOT NULL DEFAULT 0,
+ PRIMARY KEY (`id`),
+ UNIQUE KEY `realm_realmGUID` (`realm`,`realmGUID`),
+ KEY `name` (`name`),
+ KEY `idx_stub` (`stub`),
+ KEY `idx_type` (`type`),
+ KEY `idx_rating` (`rating`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Table structure for table `aowow_profiler_arena_team_member`
+--
+
+DROP TABLE IF EXISTS `aowow_profiler_arena_team_member`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `aowow_profiler_arena_team_member` (
+ `arenaTeamId` int(10) unsigned NOT NULL DEFAULT 0,
+ `profileId` int(10) unsigned NOT NULL DEFAULT 0,
+ `captain` tinyint(3) unsigned NOT NULL DEFAULT 0,
+ `weekGames` smallint(5) unsigned NOT NULL DEFAULT 0,
+ `weekWins` smallint(5) unsigned NOT NULL DEFAULT 0,
+ `seasonGames` smallint(5) unsigned NOT NULL DEFAULT 0,
+ `seasonWins` smallint(5) unsigned NOT NULL DEFAULT 0,
+ `personalRating` smallint(5) unsigned NOT NULL DEFAULT 0,
+ PRIMARY KEY (`arenaTeamId`,`profileId`),
+ KEY `guid` (`profileId`),
+ CONSTRAINT `FK_aowow_profiler_arena_team_member_aowow_profiler_arena_team` FOREIGN KEY (`arenaTeamId`) REFERENCES `aowow_profiler_arena_team` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
+ CONSTRAINT `FK_aowow_profiler_arena_team_member_aowow_profiler_profiles` FOREIGN KEY (`profileId`) REFERENCES `aowow_profiler_profiles` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Table structure for table `aowow_profiler_completion_achievements`
+--
+
+DROP TABLE IF EXISTS `aowow_profiler_completion_achievements`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `aowow_profiler_completion_achievements` (
+ `id` int(10) unsigned NOT NULL,
+ `achievementId` smallint(5) unsigned NOT NULL,
+ `date` int(10) unsigned DEFAULT NULL,
+ KEY `id` (`id`),
+ KEY `typeId` (`achievementId`),
+ CONSTRAINT `FK_pr_completion_achievements` FOREIGN KEY (`id`) REFERENCES `aowow_profiler_profiles` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Table structure for table `aowow_profiler_completion_quests`
+--
+
+DROP TABLE IF EXISTS `aowow_profiler_completion_quests`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `aowow_profiler_completion_quests` (
+ `id` int(10) unsigned NOT NULL,
+ `questId` mediumint(8) unsigned NOT NULL,
+ KEY `id` (`id`),
+ KEY `typeId` (`questId`),
+ CONSTRAINT `FK_pr_completion_quests` FOREIGN KEY (`id`) REFERENCES `aowow_profiler_profiles` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Table structure for table `aowow_profiler_completion_reputation`
+--
+
+DROP TABLE IF EXISTS `aowow_profiler_completion_reputation`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `aowow_profiler_completion_reputation` (
+ `id` int(10) unsigned NOT NULL,
+ `factionId` smallint(5) unsigned NOT NULL,
+ `standing` mediumint(9) DEFAULT NULL,
+ `exalted` tinyint(1) GENERATED ALWAYS AS (`standing` >= 42000) STORED,
+ KEY `id` (`id`),
+ KEY `typeId` (`factionId`),
+ KEY `idx_exalted` (`exalted`),
+ CONSTRAINT `FK_pr_completion_reputation` FOREIGN KEY (`id`) REFERENCES `aowow_profiler_profiles` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Table structure for table `aowow_profiler_completion_skills`
+--
+
+DROP TABLE IF EXISTS `aowow_profiler_completion_skills`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `aowow_profiler_completion_skills` (
+ `id` int(10) unsigned NOT NULL,
+ `skillId` smallint(5) unsigned NOT NULL,
+ `value` smallint(5) unsigned DEFAULT NULL,
+ `max` smallint(5) unsigned DEFAULT NULL,
+ KEY `id` (`id`),
+ KEY `typeId` (`skillId`),
+ KEY `idx_value` (`value`),
+ CONSTRAINT `FK_pr_completion_skills` FOREIGN KEY (`id`) REFERENCES `aowow_profiler_profiles` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Table structure for table `aowow_profiler_completion_spells`
+--
+
+DROP TABLE IF EXISTS `aowow_profiler_completion_spells`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `aowow_profiler_completion_spells` (
+ `id` int(10) unsigned NOT NULL,
+ `spellId` mediumint(8) unsigned NOT NULL,
+ KEY `id` (`id`),
+ KEY `typeId` (`spellId`),
+ CONSTRAINT `FK_pr_completion_spells` FOREIGN KEY (`id`) REFERENCES `aowow_profiler_profiles` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Table structure for table `aowow_profiler_completion_statistics`
+--
+
+DROP TABLE IF EXISTS `aowow_profiler_completion_statistics`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `aowow_profiler_completion_statistics` (
+ `id` int(10) unsigned NOT NULL,
+ `achievementId` smallint(6) NOT NULL,
+ `date` int(10) unsigned DEFAULT NULL,
+ `counter` smallint(5) unsigned DEFAULT NULL,
+ KEY `id` (`id`),
+ KEY `typeId` (`achievementId`),
+ CONSTRAINT `FK_pr_completion_statistics` FOREIGN KEY (`id`) REFERENCES `aowow_profiler_profiles` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Table structure for table `aowow_profiler_completion_titles`
+--
+
+DROP TABLE IF EXISTS `aowow_profiler_completion_titles`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `aowow_profiler_completion_titles` (
+ `id` int(10) unsigned NOT NULL,
+ `titleId` tinyint(3) unsigned NOT NULL,
+ KEY `id` (`id`),
+ KEY `typeId` (`titleId`),
+ CONSTRAINT `FK_pr_completion_titles` FOREIGN KEY (`id`) REFERENCES `aowow_profiler_profiles` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Table structure for table `aowow_profiler_excludes`
+--
+
+DROP TABLE IF EXISTS `aowow_profiler_excludes`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `aowow_profiler_excludes` (
+ `type` smallint(5) unsigned NOT NULL,
+ `typeId` mediumint(8) unsigned NOT NULL,
+ `groups` smallint(5) unsigned NOT NULL COMMENT 'see exclude group defines',
+ `comment` varchar(50) NOT NULL COMMENT 'rebuilding profiler files will delete everything without a comment',
+ PRIMARY KEY (`type`,`typeId`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Table structure for table `aowow_profiler_guild`
+--
+
+DROP TABLE IF EXISTS `aowow_profiler_guild`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `aowow_profiler_guild` (
+ `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
+ `realm` int(10) unsigned NOT NULL,
+ `realmGUID` int(10) unsigned NOT NULL,
+ `cuFlags` int(10) unsigned NOT NULL DEFAULT 0 COMMENT 'see defines.php for flags',
+ `stub` tinyint(1) DEFAULT 0 COMMENT 'guild stub needs resync',
+ `name` varchar(26) NOT NULL,
+ `nameUrl` varchar(26) NOT NULL,
+ `emblemStyle` tinyint(3) unsigned NOT NULL DEFAULT 0,
+ `emblemColor` tinyint(3) unsigned NOT NULL DEFAULT 0,
+ `borderStyle` tinyint(3) unsigned NOT NULL DEFAULT 0,
+ `borderColor` tinyint(3) unsigned NOT NULL DEFAULT 0,
+ `backgroundColor` tinyint(3) unsigned NOT NULL DEFAULT 0,
+ `info` varchar(500) NOT NULL DEFAULT '',
+ `createDate` int(10) unsigned NOT NULL DEFAULT 0,
+ PRIMARY KEY (`id`),
+ UNIQUE KEY `realm_realmGUID` (`realm`,`realmGUID`),
+ KEY `name` (`name`),
+ KEY `idx_stub` (`stub`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Table structure for table `aowow_profiler_guild_rank`
+--
+
+DROP TABLE IF EXISTS `aowow_profiler_guild_rank`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `aowow_profiler_guild_rank` (
+ `guildId` int(10) unsigned NOT NULL DEFAULT 0,
+ `rank` tinyint(3) unsigned NOT NULL,
+ `name` varchar(20) NOT NULL DEFAULT '',
+ PRIMARY KEY (`guildId`,`rank`),
+ KEY `rank` (`rank`),
+ CONSTRAINT `FK_aowow_profiler_guild_rank_aowow_profiler_guild` FOREIGN KEY (`guildId`) REFERENCES `aowow_profiler_guild` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Table structure for table `aowow_profiler_items`
+--
+
+DROP TABLE IF EXISTS `aowow_profiler_items`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `aowow_profiler_items` (
+ `id` int(10) unsigned DEFAULT NULL,
+ `slot` tinyint(3) unsigned DEFAULT NULL,
+ `item` mediumint(8) unsigned DEFAULT NULL,
+ `subItem` smallint(6) DEFAULT NULL,
+ `permEnchant` mediumint(8) unsigned DEFAULT NULL,
+ `tempEnchant` mediumint(8) unsigned DEFAULT NULL,
+ `extraSocket` tinyint(3) unsigned DEFAULT NULL COMMENT 'not used .. the appropriate gem slot is set to -1 instead',
+ `gem1` mediumint(9) DEFAULT NULL,
+ `gem2` mediumint(9) DEFAULT NULL,
+ `gem3` mediumint(9) DEFAULT NULL,
+ `gem4` mediumint(9) DEFAULT NULL,
+ UNIQUE KEY `id_slot` (`id`,`slot`),
+ KEY `id` (`id`),
+ KEY `item` (`item`),
+ CONSTRAINT `FK_pr_items` FOREIGN KEY (`id`) REFERENCES `aowow_profiler_profiles` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Table structure for table `aowow_profiler_pets`
+--
+
+DROP TABLE IF EXISTS `aowow_profiler_pets`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `aowow_profiler_pets` (
+ `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT,
+ `owner` int(10) unsigned DEFAULT NULL,
+ `name` varchar(50) DEFAULT NULL,
+ `family` tinyint(3) unsigned DEFAULT NULL,
+ `npc` smallint(5) unsigned DEFAULT NULL,
+ `displayId` smallint(5) unsigned DEFAULT NULL,
+ `talents` varchar(22) DEFAULT NULL,
+ PRIMARY KEY (`id`),
+ KEY `owner` (`owner`),
+ CONSTRAINT `FK_pr_pets` FOREIGN KEY (`owner`) REFERENCES `aowow_profiler_profiles` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Table structure for table `aowow_profiler_profiles`
+--
+
+DROP TABLE IF EXISTS `aowow_profiler_profiles`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `aowow_profiler_profiles` (
+ `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
+ `realm` tinyint(3) unsigned DEFAULT NULL,
+ `realmGUID` int(10) unsigned DEFAULT NULL,
+ `cuFlags` int(10) unsigned NOT NULL DEFAULT 0 COMMENT 'see defines.php for flags',
+ `custom` tinyint(1) DEFAULT 0 COMMENT 'custom profile',
+ `stub` tinyint(1) DEFAULT 0 COMMENT 'profile stub needs resync',
+ `deleted` tinyint(1) DEFAULT 0 COMMENT 'only on custom profiles',
+ `sourceId` int(10) unsigned DEFAULT NULL,
+ `sourceName` varchar(50) DEFAULT NULL,
+ `copy` int(10) unsigned DEFAULT NULL,
+ `icon` varchar(50) DEFAULT NULL,
+ `user` int(10) unsigned DEFAULT NULL,
+ `name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL,
+ `renameItr` tinyint(3) unsigned DEFAULT NULL,
+ `race` tinyint(3) unsigned NOT NULL,
+ `class` tinyint(3) unsigned NOT NULL,
+ `level` tinyint(3) unsigned NOT NULL,
+ `gender` tinyint(3) unsigned NOT NULL,
+ `guild` int(10) unsigned DEFAULT NULL,
+ `guildrank` tinyint(3) unsigned DEFAULT NULL COMMENT '0: guild master',
+ `skincolor` tinyint(3) unsigned NOT NULL DEFAULT 0,
+ `hairstyle` tinyint(3) unsigned NOT NULL DEFAULT 0,
+ `haircolor` tinyint(3) unsigned NOT NULL DEFAULT 0,
+ `facetype` tinyint(3) unsigned NOT NULL DEFAULT 0,
+ `features` tinyint(3) unsigned NOT NULL DEFAULT 0,
+ `nomodelMask` int(10) unsigned NOT NULL DEFAULT 0,
+ `title` tinyint(3) unsigned NOT NULL DEFAULT 0,
+ `description` text DEFAULT NULL,
+ `playedtime` int(10) unsigned NOT NULL DEFAULT 0,
+ `gearscore` smallint(5) unsigned NOT NULL DEFAULT 0,
+ `achievementpoints` smallint(5) unsigned NOT NULL DEFAULT 0,
+ `lastupdated` int(11) NOT NULL DEFAULT 0,
+ `talenttree1` tinyint(3) unsigned NOT NULL DEFAULT 0 COMMENT 'points spend in 1st tree',
+ `talenttree2` tinyint(3) unsigned NOT NULL DEFAULT 0 COMMENT 'points spend in 2nd tree',
+ `talenttree3` tinyint(3) unsigned NOT NULL DEFAULT 0 COMMENT 'points spend in 3rd tree',
+ `talentbuild1` varchar(105) NOT NULL DEFAULT '',
+ `talentbuild2` varchar(105) NOT NULL DEFAULT '',
+ `glyphs1` varchar(45) NOT NULL DEFAULT '',
+ `glyphs2` varchar(45) NOT NULL DEFAULT '',
+ `activespec` tinyint(3) unsigned NOT NULL DEFAULT 0,
+ PRIMARY KEY (`id`),
+ UNIQUE KEY `realm_realmGUID` (`realm`,`realmGUID`),
+ KEY `user` (`user`),
+ KEY `guild` (`guild`),
+ KEY `name` (`name`),
+ KEY `idx_custom` (`custom`),
+ KEY `idx_stub` (`stub`),
+ KEY `idx_deleted` (`deleted`),
+ KEY `idx_race` (`race`),
+ KEY `idx_class` (`class`),
+ KEY `idx_level` (`level`),
+ KEY `idx_guildrank` (`guildrank`),
+ KEY `idx_gearscore` (`gearscore`),
+ KEY `idx_achievementpoints` (`achievementpoints`),
+ KEY `idx_talenttree1` (`talenttree1`),
+ KEY `idx_talenttree2` (`talenttree2`),
+ KEY `idx_talenttree3` (`talenttree3`),
+ CONSTRAINT `FK_aowow_profiler_profiles_aowow_profiler_guild` FOREIGN KEY (`guild`) REFERENCES `aowow_profiler_guild` (`id`) ON DELETE SET NULL ON UPDATE CASCADE
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Table structure for table `aowow_profiler_sync`
+--
+
+DROP TABLE IF EXISTS `aowow_profiler_sync`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `aowow_profiler_sync` (
+ `realm` tinyint(3) unsigned NOT NULL,
+ `realmGUID` int(10) unsigned NOT NULL,
+ `type` smallint(5) unsigned NOT NULL,
+ `typeId` int(10) unsigned NOT NULL,
+ `requestTime` int(10) unsigned NOT NULL,
+ `status` tinyint(3) unsigned NOT NULL,
+ `errorCode` tinyint(3) unsigned NOT NULL DEFAULT 0,
+ UNIQUE KEY `realm_realmGUID_type_typeId` (`realm`,`realmGUID`,`type`),
+ UNIQUE KEY `type_typeId` (`type`,`typeId`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Table structure for table `aowow_quests`
+--
+
+DROP TABLE IF EXISTS `aowow_quests`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `aowow_quests` (
+ `id` mediumint(8) unsigned NOT NULL DEFAULT 0,
+ `questType` tinyint(3) unsigned NOT NULL DEFAULT 2,
+ `level` smallint(6) NOT NULL DEFAULT 1,
+ `minLevel` tinyint(3) unsigned NOT NULL DEFAULT 0,
+ `maxLevel` tinyint(3) unsigned NOT NULL DEFAULT 0,
+ `questSortId` smallint(6) NOT NULL DEFAULT 0,
+ `questSortIdBak` smallint(6) NOT NULL DEFAULT 0,
+ `questInfoId` smallint(5) unsigned NOT NULL DEFAULT 0,
+ `suggestedPlayers` tinyint(3) unsigned NOT NULL DEFAULT 0,
+ `timeLimit` int(10) unsigned NOT NULL DEFAULT 0,
+ `eventId` smallint(5) unsigned NOT NULL DEFAULT 0,
+ `prevQuestId` mediumint(9) NOT NULL DEFAULT 0,
+ `nextQuestId` mediumint(9) NOT NULL DEFAULT 0,
+ `breadcrumbForQuestId` mediumint(9) NOT NULL DEFAULT 0,
+ `exclusiveGroup` mediumint(9) NOT NULL DEFAULT 0,
+ `nextQuestIdChain` mediumint(8) unsigned NOT NULL DEFAULT 0,
+ `flags` int(10) unsigned NOT NULL DEFAULT 0,
+ `specialFlags` tinyint(3) unsigned NOT NULL DEFAULT 0,
+ `cuFlags` int(10) unsigned NOT NULL DEFAULT 0 COMMENT 'see defines.php for flags',
+ `reqClassMask` smallint(5) unsigned NOT NULL DEFAULT 0,
+ `reqRaceMask` smallint(5) unsigned NOT NULL DEFAULT 0,
+ `reqSkillId` smallint(5) unsigned NOT NULL DEFAULT 0,
+ `reqSkillPoints` smallint(5) unsigned NOT NULL DEFAULT 0,
+ `reqFactionId1` smallint(5) unsigned NOT NULL DEFAULT 0,
+ `reqFactionId2` smallint(5) unsigned NOT NULL DEFAULT 0,
+ `reqFactionValue1` mediumint(9) NOT NULL DEFAULT 0,
+ `reqFactionValue2` mediumint(9) NOT NULL DEFAULT 0,
+ `reqMinRepFaction` smallint(5) unsigned NOT NULL DEFAULT 0,
+ `reqMaxRepFaction` smallint(5) unsigned NOT NULL DEFAULT 0,
+ `reqMinRepValue` mediumint(9) NOT NULL DEFAULT 0,
+ `reqMaxRepValue` mediumint(9) NOT NULL DEFAULT 0,
+ `reqPlayerKills` tinyint(3) unsigned NOT NULL DEFAULT 0,
+ `sourceItemId` mediumint(8) unsigned NOT NULL DEFAULT 0,
+ `sourceItemCount` tinyint(3) unsigned NOT NULL DEFAULT 0,
+ `sourceSpellId` mediumint(8) unsigned NOT NULL DEFAULT 0,
+ `rewardXP` mediumint(8) unsigned NOT NULL DEFAULT 0,
+ `rewardOrReqMoney` int(11) NOT NULL DEFAULT 0,
+ `rewardMoneyMaxLevel` int(10) unsigned NOT NULL DEFAULT 0,
+ `rewardSpell` mediumint(8) unsigned NOT NULL DEFAULT 0,
+ `rewardSpellCast` int(11) NOT NULL DEFAULT 0,
+ `rewardHonorPoints` int(11) NOT NULL DEFAULT 0,
+ `rewardMailTemplateId` mediumint(8) unsigned NOT NULL DEFAULT 0,
+ `rewardMailDelay` int(10) unsigned NOT NULL DEFAULT 0,
+ `rewardTitleId` tinyint(3) unsigned NOT NULL DEFAULT 0,
+ `rewardTalents` tinyint(3) unsigned NOT NULL DEFAULT 0,
+ `rewardArenaPoints` smallint(6) NOT NULL DEFAULT 0,
+ `rewardItemId1` mediumint(8) unsigned NOT NULL DEFAULT 0,
+ `rewardItemId2` mediumint(8) unsigned NOT NULL DEFAULT 0,
+ `rewardItemId3` mediumint(8) unsigned NOT NULL DEFAULT 0,
+ `rewardItemId4` mediumint(8) unsigned NOT NULL DEFAULT 0,
+ `rewardItemCount1` smallint(5) unsigned NOT NULL DEFAULT 0,
+ `rewardItemCount2` smallint(5) unsigned NOT NULL DEFAULT 0,
+ `rewardItemCount3` smallint(5) unsigned NOT NULL DEFAULT 0,
+ `rewardItemCount4` smallint(5) unsigned NOT NULL DEFAULT 0,
+ `rewardChoiceItemId1` mediumint(8) unsigned NOT NULL DEFAULT 0,
+ `rewardChoiceItemId2` mediumint(8) unsigned NOT NULL DEFAULT 0,
+ `rewardChoiceItemId3` mediumint(8) unsigned NOT NULL DEFAULT 0,
+ `rewardChoiceItemId4` mediumint(8) unsigned NOT NULL DEFAULT 0,
+ `rewardChoiceItemId5` mediumint(8) unsigned NOT NULL DEFAULT 0,
+ `rewardChoiceItemId6` mediumint(8) unsigned NOT NULL DEFAULT 0,
+ `rewardChoiceItemCount1` smallint(5) unsigned NOT NULL DEFAULT 0,
+ `rewardChoiceItemCount2` smallint(5) unsigned NOT NULL DEFAULT 0,
+ `rewardChoiceItemCount3` smallint(5) unsigned NOT NULL DEFAULT 0,
+ `rewardChoiceItemCount4` smallint(5) unsigned NOT NULL DEFAULT 0,
+ `rewardChoiceItemCount5` smallint(5) unsigned NOT NULL DEFAULT 0,
+ `rewardChoiceItemCount6` smallint(5) unsigned NOT NULL DEFAULT 0,
+ `rewardFactionId1` smallint(5) unsigned NOT NULL DEFAULT 0 COMMENT 'faction id from Faction.dbc in this case',
+ `rewardFactionId2` smallint(5) unsigned NOT NULL DEFAULT 0 COMMENT 'faction id from Faction.dbc in this case',
+ `rewardFactionId3` smallint(5) unsigned NOT NULL DEFAULT 0 COMMENT 'faction id from Faction.dbc in this case',
+ `rewardFactionId4` smallint(5) unsigned NOT NULL DEFAULT 0 COMMENT 'faction id from Faction.dbc in this case',
+ `rewardFactionId5` smallint(5) unsigned NOT NULL DEFAULT 0 COMMENT 'faction id from Faction.dbc in this case',
+ `rewardFactionValue1` mediumint(9) NOT NULL DEFAULT 0,
+ `rewardFactionValue2` mediumint(9) NOT NULL DEFAULT 0,
+ `rewardFactionValue3` mediumint(9) NOT NULL DEFAULT 0,
+ `rewardFactionValue4` mediumint(9) NOT NULL DEFAULT 0,
+ `rewardFactionValue5` mediumint(9) NOT NULL DEFAULT 0,
+ `name_loc0` varchar(100) DEFAULT NULL,
+ `name_loc2` varchar(100) DEFAULT NULL,
+ `name_loc3` varchar(100) DEFAULT NULL,
+ `name_loc4` varchar(100) DEFAULT NULL,
+ `name_loc6` varchar(100) DEFAULT NULL,
+ `name_loc8` varchar(100) DEFAULT NULL,
+ `objectives_loc0` text DEFAULT NULL,
+ `objectives_loc2` text DEFAULT NULL,
+ `objectives_loc3` text DEFAULT NULL,
+ `objectives_loc4` text DEFAULT NULL,
+ `objectives_loc6` text DEFAULT NULL,
+ `objectives_loc8` text DEFAULT NULL,
+ `details_loc0` text DEFAULT NULL,
+ `details_loc2` text DEFAULT NULL,
+ `details_loc3` text DEFAULT NULL,
+ `details_loc4` text DEFAULT NULL,
+ `details_loc6` text DEFAULT NULL,
+ `details_loc8` text DEFAULT NULL,
+ `end_loc0` text DEFAULT NULL,
+ `end_loc2` text DEFAULT NULL,
+ `end_loc3` text DEFAULT NULL,
+ `end_loc4` text DEFAULT NULL,
+ `end_loc6` text DEFAULT NULL,
+ `end_loc8` text DEFAULT NULL,
+ `offerReward_loc0` text DEFAULT NULL,
+ `offerReward_loc2` text DEFAULT NULL,
+ `offerReward_loc3` text DEFAULT NULL,
+ `offerReward_loc4` text DEFAULT NULL,
+ `offerReward_loc6` text DEFAULT NULL,
+ `offerReward_loc8` text DEFAULT NULL,
+ `requestItems_loc0` text DEFAULT NULL,
+ `requestItems_loc2` text DEFAULT NULL,
+ `requestItems_loc3` text DEFAULT NULL,
+ `requestItems_loc4` text DEFAULT NULL,
+ `requestItems_loc6` text DEFAULT NULL,
+ `requestItems_loc8` text DEFAULT NULL,
+ `completed_loc0` text DEFAULT NULL,
+ `completed_loc2` text DEFAULT NULL,
+ `completed_loc3` text DEFAULT NULL,
+ `completed_loc4` text DEFAULT NULL,
+ `completed_loc6` text DEFAULT NULL,
+ `completed_loc8` text DEFAULT NULL,
+ `reqNpcOrGo1` mediumint(9) NOT NULL DEFAULT 0,
+ `reqNpcOrGo2` mediumint(9) NOT NULL DEFAULT 0,
+ `reqNpcOrGo3` mediumint(9) NOT NULL DEFAULT 0,
+ `reqNpcOrGo4` mediumint(9) NOT NULL DEFAULT 0,
+ `reqNpcOrGoCount1` smallint(5) unsigned NOT NULL DEFAULT 0,
+ `reqNpcOrGoCount2` smallint(5) unsigned NOT NULL DEFAULT 0,
+ `reqNpcOrGoCount3` smallint(5) unsigned NOT NULL DEFAULT 0,
+ `reqNpcOrGoCount4` smallint(5) unsigned NOT NULL DEFAULT 0,
+ `reqSourceItemId1` mediumint(8) unsigned NOT NULL DEFAULT 0,
+ `reqSourceItemId2` mediumint(8) unsigned NOT NULL DEFAULT 0,
+ `reqSourceItemId3` mediumint(8) unsigned NOT NULL DEFAULT 0,
+ `reqSourceItemId4` mediumint(8) unsigned NOT NULL DEFAULT 0,
+ `reqSourceItemCount1` smallint(5) unsigned NOT NULL DEFAULT 0,
+ `reqSourceItemCount2` smallint(5) unsigned NOT NULL DEFAULT 0,
+ `reqSourceItemCount3` smallint(5) unsigned NOT NULL DEFAULT 0,
+ `reqSourceItemCount4` smallint(5) unsigned NOT NULL DEFAULT 0,
+ `reqItemId1` mediumint(8) unsigned NOT NULL DEFAULT 0,
+ `reqItemId2` mediumint(8) unsigned NOT NULL DEFAULT 0,
+ `reqItemId3` mediumint(8) unsigned NOT NULL DEFAULT 0,
+ `reqItemId4` mediumint(8) unsigned NOT NULL DEFAULT 0,
+ `reqItemId5` mediumint(8) unsigned NOT NULL DEFAULT 0,
+ `reqItemId6` mediumint(8) unsigned NOT NULL DEFAULT 0,
+ `reqItemCount1` smallint(5) unsigned NOT NULL DEFAULT 0,
+ `reqItemCount2` smallint(5) unsigned NOT NULL DEFAULT 0,
+ `reqItemCount3` smallint(5) unsigned NOT NULL DEFAULT 0,
+ `reqItemCount4` smallint(5) unsigned NOT NULL DEFAULT 0,
+ `reqItemCount5` smallint(5) unsigned NOT NULL DEFAULT 0,
+ `reqItemCount6` smallint(5) unsigned NOT NULL DEFAULT 0,
+ `objectiveText1_loc0` text DEFAULT NULL,
+ `objectiveText1_loc2` text DEFAULT NULL,
+ `objectiveText1_loc3` text DEFAULT NULL,
+ `objectiveText1_loc4` text DEFAULT NULL,
+ `objectiveText1_loc6` text DEFAULT NULL,
+ `objectiveText1_loc8` text DEFAULT NULL,
+ `objectiveText2_loc0` text DEFAULT NULL,
+ `objectiveText2_loc2` text DEFAULT NULL,
+ `objectiveText2_loc3` text DEFAULT NULL,
+ `objectiveText2_loc4` text DEFAULT NULL,
+ `objectiveText2_loc6` text DEFAULT NULL,
+ `objectiveText2_loc8` text DEFAULT NULL,
+ `objectiveText3_loc0` text DEFAULT NULL,
+ `objectiveText3_loc2` text DEFAULT NULL,
+ `objectiveText3_loc3` text DEFAULT NULL,
+ `objectiveText3_loc4` text DEFAULT NULL,
+ `objectiveText3_loc6` text DEFAULT NULL,
+ `objectiveText3_loc8` text DEFAULT NULL,
+ `objectiveText4_loc0` text DEFAULT NULL,
+ `objectiveText4_loc2` text DEFAULT NULL,
+ `objectiveText4_loc3` text DEFAULT NULL,
+ `objectiveText4_loc4` text DEFAULT NULL,
+ `objectiveText4_loc6` text DEFAULT NULL,
+ `objectiveText4_loc8` text DEFAULT NULL,
+ PRIMARY KEY (`id`),
+ KEY `nextQuestIdChain` (`nextQuestIdChain`),
+ KEY `idx_name0` (`name_loc0`),
+ KEY `idx_name2` (`name_loc2`),
+ KEY `idx_name3` (`name_loc3`),
+ KEY `idx_name4` (`name_loc4`),
+ KEY `idx_name6` (`name_loc6`),
+ KEY `idx_name8` (`name_loc8`),
+ KEY `idx_sourcespell` (`sourceSpellId`),
+ KEY `idx_rewardspell` (`rewardSpell`),
+ KEY `idx_rewardcastspell` (`rewardSpellCast`),
+ KEY `idx_classmask` (`reqRaceMask`),
+ KEY `idx_racemask` (`reqClassMask`),
+ KEY `idx_questsort` (`questSortId`),
+ KEY `idx_rewarditem1` (`rewardChoiceItemId1`),
+ KEY `idx_rewarditem2` (`rewardChoiceItemId2`),
+ KEY `idx_rewarditem3` (`rewardChoiceItemId3`),
+ KEY `idx_rewarditem4` (`rewardChoiceItemId4`),
+ KEY `idx_rewarditem5` (`rewardChoiceItemId5`),
+ KEY `idx_rewarditem6` (`rewardChoiceItemId6`),
+ KEY `idx_rewardfaction1` (`rewardFactionId1`),
+ KEY `idx_rewardfaction2` (`rewardFactionId2`),
+ KEY `idx_rewardfaction3` (`rewardFactionId3`),
+ KEY `idx_rewardfaction4` (`rewardFactionId4`),
+ KEY `idx_rewardfaction5` (`rewardFactionId5`),
+ KEY `idx_choiceitem1` (`rewardItemId1`),
+ KEY `idx_choiceitem2` (`rewardItemId2`),
+ KEY `idx_choiceitem3` (`rewardItemId3`),
+ KEY `idx_choiceitem4` (`rewardItemId4`),
+ KEY `idx_requirement1` (`reqNpcOrGo1`),
+ KEY `idx_requirement2` (`reqNpcOrGo2`),
+ KEY `idx_requirement3` (`reqNpcOrGo3`),
+ KEY `idx_requirement4` (`reqNpcOrGo4`),
+ KEY `idx_event` (`eventId`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Table structure for table `aowow_quests_search`
+--
+
+DROP TABLE IF EXISTS `aowow_quests_search`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `aowow_quests_search` (
+ `id` mediumint(8) unsigned NOT NULL,
+ `locale` tinyint(3) unsigned NOT NULL,
+ `nName` varchar(100) DEFAULT NULL,
+ `nObjectives` text DEFAULT NULL,
+ `nDetails` text DEFAULT NULL,
+ PRIMARY KEY (`id`,`locale`),
+ FULLTEXT KEY `idx_ft_na` (`nName`),
+ FULLTEXT KEY `idx_ft_na_ex` (`nName`,`nObjectives`,`nDetails`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Table structure for table `aowow_quests_startend`
+--
+
+DROP TABLE IF EXISTS `aowow_quests_startend`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `aowow_quests_startend` (
+ `type` tinyint(3) unsigned NOT NULL,
+ `typeId` mediumint(8) unsigned NOT NULL,
+ `questId` mediumint(8) unsigned NOT NULL,
+ `method` tinyint(3) unsigned NOT NULL COMMENT '&0x1: starts; &0x2:ends',
+ `eventId` smallint(5) unsigned NOT NULL DEFAULT 0,
+ PRIMARY KEY (`type`,`typeId`,`questId`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Table structure for table `aowow_quickfacts`
+--
+
+DROP TABLE IF EXISTS `aowow_quickfacts`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `aowow_quickfacts` (
+ `type` smallint(5) unsigned NOT NULL,
+ `typeId` mediumint(9) NOT NULL,
+ `orderIdx` tinyint(4) NOT NULL COMMENT '<0: prepend to generic list; >0: append to generic list',
+ `row` varchar(200) NOT NULL COMMENT 'Markdown formated',
+ UNIQUE KEY `row` (`type`,`typeId`,`orderIdx`) USING BTREE
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=COMPACT;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Table structure for table `aowow_races`
+--
+
+DROP TABLE IF EXISTS `aowow_races`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `aowow_races` (
+ `id` int(10) unsigned NOT NULL,
+ `classMask` smallint(5) unsigned NOT NULL,
+ `flags` tinyint(3) unsigned NOT NULL,
+ `cuFlags` int(10) unsigned NOT NULL DEFAULT 0 COMMENT 'see defines.php for flags',
+ `factionId` smallint(6) NOT NULL,
+ `startAreaId` smallint(6) NOT NULL,
+ `leader` mediumint(8) unsigned NOT NULL,
+ `baseLanguage` tinyint(3) unsigned NOT NULL,
+ `side` tinyint(3) unsigned NOT NULL,
+ `fileString` varchar(64) DEFAULT NULL,
+ `iconId0` smallint(5) unsigned NOT NULL DEFAULT 0 COMMENT 'male icon',
+ `iconId1` smallint(5) unsigned NOT NULL DEFAULT 0 COMMENT 'female icon',
+ `name_loc0` varchar(64) DEFAULT NULL,
+ `name_loc2` varchar(64) DEFAULT NULL,
+ `name_loc3` varchar(64) DEFAULT NULL,
+ `name_loc4` varchar(64) DEFAULT NULL,
+ `name_loc6` varchar(64) DEFAULT NULL,
+ `name_loc8` varchar(64) DEFAULT NULL,
+ `expansion` int(11) NOT NULL,
+ PRIMARY KEY (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Table structure for table `aowow_races_sounds`
+--
+
+DROP TABLE IF EXISTS `aowow_races_sounds`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `aowow_races_sounds` (
+ `raceId` tinyint(3) unsigned NOT NULL,
+ `soundId` smallint(5) unsigned NOT NULL,
+ `gender` tinyint(3) unsigned NOT NULL,
+ UNIQUE KEY `race_soundId_gender` (`raceId`,`soundId`,`gender`),
+ KEY `race` (`raceId`),
+ KEY `soundId` (`soundId`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Table structure for table `aowow_reports`
+--
+
+DROP TABLE IF EXISTS `aowow_reports`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `aowow_reports` (
+ `id` mediumint(8) unsigned NOT NULL AUTO_INCREMENT,
+ `userId` mediumint(8) unsigned NOT NULL,
+ `assigned` mediumint(8) unsigned NOT NULL DEFAULT 0,
+ `status` tinyint(3) unsigned NOT NULL DEFAULT 0 COMMENT '0:new; 1:solved; 2:rejected',
+ `createDate` int(10) unsigned NOT NULL,
+ `mode` tinyint(3) unsigned NOT NULL,
+ `reason` tinyint(3) unsigned NOT NULL,
+ `subject` mediumint(9) NOT NULL DEFAULT 0,
+ `ip` varchar(50) NOT NULL,
+ `description` text NOT NULL,
+ `userAgent` varchar(255) NOT NULL,
+ `appName` varchar(32) NOT NULL,
+ `url` varchar(255) NOT NULL,
+ `relatedUrl` varchar(255) DEFAULT NULL,
+ `email` varchar(255) DEFAULT NULL,
+ PRIMARY KEY (`id`),
+ KEY `userId` (`userId`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Table structure for table `aowow_screeneffect_sounds`
+--
+
+DROP TABLE IF EXISTS `aowow_screeneffect_sounds`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `aowow_screeneffect_sounds` (
+ `id` smallint(5) unsigned NOT NULL,
+ `name` varchar(40) NOT NULL,
+ `ambienceDay` smallint(5) unsigned NOT NULL,
+ `ambienceNight` smallint(5) unsigned NOT NULL,
+ `musicDay` smallint(5) unsigned NOT NULL,
+ `musicNight` smallint(5) unsigned NOT NULL,
+ KEY `id` (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Table structure for table `aowow_screenshots`
+--
+
+DROP TABLE IF EXISTS `aowow_screenshots`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `aowow_screenshots` (
+ `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
+ `type` smallint(5) unsigned NOT NULL,
+ `typeId` mediumint(9) NOT NULL,
+ `userIdOwner` int(10) unsigned DEFAULT NULL,
+ `date` int(10) unsigned NOT NULL,
+ `width` smallint(5) unsigned NOT NULL,
+ `height` smallint(5) unsigned NOT NULL,
+ `caption` varchar(200) DEFAULT NULL,
+ `status` tinyint(3) unsigned NOT NULL COMMENT 'see defines.php - CC_FLAG_*',
+ `userIdApprove` int(10) unsigned DEFAULT NULL,
+ `userIdDelete` int(10) unsigned DEFAULT NULL,
+ PRIMARY KEY (`id`),
+ KEY `type` (`type`,`typeId`),
+ KEY `FK_acc_ss` (`userIdOwner`),
+ CONSTRAINT `FK_acc_ss` FOREIGN KEY (`userIdOwner`) REFERENCES `aowow_account` (`id`) ON DELETE SET NULL ON UPDATE CASCADE
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=COMPACT;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Table structure for table `aowow_setup_custom_data`
+--
+
+DROP TABLE IF EXISTS `aowow_setup_custom_data`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `aowow_setup_custom_data` (
+ `command` varchar(100) NOT NULL DEFAULT '',
+ `entry` int(11) NOT NULL DEFAULT 0 COMMENT 'typeId',
+ `field` varchar(100) NOT NULL DEFAULT '',
+ `value` text DEFAULT NULL,
+ `comment` text DEFAULT NULL,
+ KEY `aowow_setup_custom_data_command_IDX` (`command`) USING BTREE
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Table structure for table `aowow_shapeshiftforms`
+--
+
+DROP TABLE IF EXISTS `aowow_shapeshiftforms`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `aowow_shapeshiftforms` (
+ `Id` tinyint(3) unsigned NOT NULL,
+ `flags` smallint(5) unsigned NOT NULL,
+ `creatureType` tinyint(4) NOT NULL,
+ `displayIdA` smallint(5) unsigned NOT NULL,
+ `displayIdH` smallint(5) unsigned NOT NULL,
+ `spellId1` mediumint(8) unsigned NOT NULL,
+ `spellId2` mediumint(8) unsigned NOT NULL,
+ `spellId3` mediumint(8) unsigned NOT NULL,
+ `spellId4` mediumint(8) unsigned NOT NULL,
+ `spellId5` mediumint(8) unsigned NOT NULL,
+ `spellId6` mediumint(8) unsigned NOT NULL,
+ `spellId7` mediumint(8) unsigned NOT NULL,
+ `spellId8` mediumint(8) unsigned NOT NULL,
+ `comment` varchar(30) DEFAULT NULL,
+ PRIMARY KEY (`Id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Table structure for table `aowow_skillline`
+--
+
+DROP TABLE IF EXISTS `aowow_skillline`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `aowow_skillline` (
+ `Id` smallint(5) unsigned NOT NULL,
+ `typeCat` tinyint(4) NOT NULL,
+ `cuFlags` int(10) unsigned NOT NULL DEFAULT 0 COMMENT 'see defines.php for flags',
+ `categoryId` tinyint(4) NOT NULL,
+ `name_loc0` varchar(64) DEFAULT NULL,
+ `name_loc2` varchar(64) DEFAULT NULL,
+ `name_loc3` varchar(64) DEFAULT NULL,
+ `name_loc4` varchar(64) DEFAULT NULL,
+ `name_loc6` varchar(64) DEFAULT NULL,
+ `name_loc8` varchar(64) DEFAULT NULL,
+ `description_loc0` text DEFAULT NULL,
+ `description_loc2` text DEFAULT NULL,
+ `description_loc3` text DEFAULT NULL,
+ `description_loc4` text DEFAULT NULL,
+ `description_loc6` text DEFAULT NULL,
+ `description_loc8` text DEFAULT NULL,
+ `iconId` smallint(5) unsigned NOT NULL DEFAULT 0,
+ `iconIdBak` smallint(5) unsigned NOT NULL DEFAULT 0,
+ `professionMask` smallint(5) unsigned NOT NULL,
+ `recipeSubClass` tinyint(3) unsigned NOT NULL,
+ `specializations` varchar(30) NOT NULL COMMENT 'space-separated spellIds',
+ PRIMARY KEY (`Id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Table structure for table `aowow_sounds`
+--
+
+DROP TABLE IF EXISTS `aowow_sounds`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `aowow_sounds` (
+ `id` smallint(5) unsigned NOT NULL,
+ `cat` tinyint(3) unsigned NOT NULL,
+ `name` varchar(100) NOT NULL,
+ `cuFlags` int(10) unsigned NOT NULL DEFAULT 0 COMMENT 'see defines.php for flags',
+ `soundFile1` smallint(5) unsigned DEFAULT NULL,
+ `soundFile2` smallint(5) unsigned DEFAULT NULL,
+ `soundFile3` smallint(5) unsigned DEFAULT NULL,
+ `soundFile4` smallint(5) unsigned DEFAULT NULL,
+ `soundFile5` smallint(5) unsigned DEFAULT NULL,
+ `soundFile6` smallint(5) unsigned DEFAULT NULL,
+ `soundFile7` smallint(5) unsigned DEFAULT NULL,
+ `soundFile8` smallint(5) unsigned DEFAULT NULL,
+ `soundFile9` smallint(5) unsigned DEFAULT NULL,
+ `soundFile10` smallint(5) unsigned DEFAULT NULL,
+ `flags` mediumint(8) unsigned NOT NULL,
+ PRIMARY KEY (`id`),
+ KEY `cat` (`cat`),
+ KEY `name` (`name`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Table structure for table `aowow_sounds_files`
+--
+
+DROP TABLE IF EXISTS `aowow_sounds_files`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `aowow_sounds_files` (
+ `id` smallint(6) NOT NULL COMMENT '<0 not found in client files',
+ `file` varchar(75) NOT NULL,
+ `path` varchar(75) NOT NULL COMMENT 'in client',
+ `type` enum('OGG','MP3') NOT NULL,
+ PRIMARY KEY (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Table structure for table `aowow_source`
+--
+
+DROP TABLE IF EXISTS `aowow_source`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `aowow_source` (
+ `type` tinyint(3) unsigned NOT NULL,
+ `typeId` mediumint(9) NOT NULL,
+ `moreType` tinyint(3) unsigned DEFAULT NULL,
+ `moreTypeId` mediumint(8) unsigned DEFAULT NULL,
+ `moreZoneId` mediumint(8) unsigned DEFAULT NULL,
+ `moreMask` mediumint(8) unsigned DEFAULT NULL,
+ `src1` tinyint(3) unsigned DEFAULT NULL COMMENT 'Crafted',
+ `src2` tinyint(3) unsigned DEFAULT NULL COMMENT 'Drop (npc / object / item) (modeMask)',
+ `src3` tinyint(3) unsigned DEFAULT NULL COMMENT 'PvP (g_sources_pvp)',
+ `src4` tinyint(3) unsigned DEFAULT NULL COMMENT 'Quest (side)',
+ `src5` tinyint(3) unsigned DEFAULT NULL COMMENT 'Vendor',
+ `src6` tinyint(3) unsigned DEFAULT NULL COMMENT 'Trainer',
+ `src7` tinyint(3) unsigned DEFAULT NULL COMMENT 'Discovery',
+ `src8` tinyint(3) unsigned DEFAULT NULL COMMENT 'Redemption',
+ `src9` tinyint(3) unsigned DEFAULT NULL COMMENT 'Talent',
+ `src10` tinyint(3) unsigned DEFAULT NULL COMMENT 'Starter',
+ `src11` tinyint(3) unsigned DEFAULT NULL COMMENT 'Event (special; not holidays) [not used]',
+ `src12` tinyint(3) unsigned DEFAULT NULL COMMENT 'Achievemement',
+ `src13` tinyint(3) unsigned DEFAULT NULL COMMENT 'Misc Source (sourceStringId)',
+ `src14` tinyint(3) unsigned DEFAULT NULL COMMENT 'Black Market [not used]',
+ `src15` tinyint(3) unsigned DEFAULT NULL COMMENT 'Disenchanted',
+ `src16` tinyint(3) unsigned DEFAULT NULL COMMENT 'Fished',
+ `src17` tinyint(3) unsigned DEFAULT NULL COMMENT 'Gathered',
+ `src18` tinyint(3) unsigned DEFAULT NULL COMMENT 'Milled',
+ `src19` tinyint(3) unsigned DEFAULT NULL COMMENT 'Mined',
+ `src20` tinyint(3) unsigned DEFAULT NULL COMMENT 'Prospected',
+ `src21` tinyint(3) unsigned DEFAULT NULL COMMENT 'Pickpocketed',
+ `src22` tinyint(3) unsigned DEFAULT NULL COMMENT 'Salvaged',
+ `src23` tinyint(3) unsigned DEFAULT NULL COMMENT 'Skinned',
+ `src24` tinyint(3) unsigned DEFAULT NULL COMMENT 'In-Game Store [not used]',
+ PRIMARY KEY (`type`,`typeId`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Table structure for table `aowow_spawns`
+--
+
+DROP TABLE IF EXISTS `aowow_spawns`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `aowow_spawns` (
+ `guid` int(11) NOT NULL COMMENT '< 0: vehicle accessory',
+ `type` smallint(5) unsigned NOT NULL,
+ `typeId` int(10) unsigned NOT NULL,
+ `respawn` int(11) NOT NULL DEFAULT 0 COMMENT 'in seconds',
+ `spawnMask` tinyint(3) unsigned NOT NULL DEFAULT 0,
+ `phaseMask` smallint(5) unsigned NOT NULL DEFAULT 0,
+ `areaId` smallint(5) unsigned NOT NULL DEFAULT 0,
+ `floor` tinyint(3) unsigned NOT NULL DEFAULT 0,
+ `posX` float unsigned NOT NULL,
+ `posY` float unsigned NOT NULL,
+ `pathId` int(10) unsigned NOT NULL DEFAULT 0,
+ `ScriptName` varchar(64) DEFAULT NULL,
+ `StringId` varchar(64) DEFAULT NULL,
+ PRIMARY KEY (`guid`,`type`,`floor`),
+ KEY `type_idx` (`typeId`,`type`),
+ KEY `zone_idx` (`areaId`),
+ KEY `guid` (`guid`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Table structure for table `aowow_spawns_override`
+--
+
+DROP TABLE IF EXISTS `aowow_spawns_override`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `aowow_spawns_override` (
+ `type` smallint(5) unsigned NOT NULL,
+ `typeGuid` mediumint(9) NOT NULL,
+ `areaId` mediumint(8) unsigned NOT NULL,
+ `floor` mediumint(8) unsigned NOT NULL,
+ `revision` tinyint(3) unsigned NOT NULL COMMENT 'Aowow revision, when this override was applied',
+ PRIMARY KEY (`type`,`typeGuid`) USING BTREE
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Table structure for table `aowow_spell`
+--
+
+DROP TABLE IF EXISTS `aowow_spell`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `aowow_spell` (
+ `id` mediumint(8) unsigned NOT NULL,
+ `category` smallint(5) unsigned NOT NULL,
+ `dispelType` tinyint(3) unsigned NOT NULL,
+ `mechanic` tinyint(3) unsigned NOT NULL,
+ `attributes0` int(10) unsigned NOT NULL,
+ `attributes1` int(10) unsigned NOT NULL,
+ `attributes2` int(10) unsigned NOT NULL,
+ `attributes3` int(10) unsigned NOT NULL,
+ `attributes4` int(10) unsigned NOT NULL,
+ `attributes5` int(10) unsigned NOT NULL,
+ `attributes6` int(10) unsigned NOT NULL,
+ `attributes7` int(10) unsigned NOT NULL,
+ `cuFlags` int(10) unsigned NOT NULL DEFAULT 0 COMMENT 'see defines.php for flags',
+ `typeCat` smallint(6) NOT NULL,
+ `stanceMask` int(11) NOT NULL,
+ `stanceMaskNot` int(11) NOT NULL,
+ `targets` mediumint(8) unsigned NOT NULL,
+ `spellFocusObject` smallint(5) unsigned NOT NULL,
+ `castTime` float unsigned NOT NULL,
+ `recoveryTime` int(10) unsigned NOT NULL,
+ `recoveryCategory` int(10) unsigned NOT NULL,
+ `startRecoveryTime` mediumint(8) unsigned NOT NULL,
+ `startRecoveryCategory` smallint(5) unsigned NOT NULL,
+ `procChance` tinyint(3) unsigned NOT NULL,
+ `procCharges` mediumint(8) unsigned NOT NULL,
+ `procCustom` float NOT NULL,
+ `procCooldown` smallint(5) unsigned NOT NULL,
+ `maxLevel` smallint(5) unsigned NOT NULL,
+ `baseLevel` smallint(5) unsigned NOT NULL,
+ `spellLevel` smallint(5) unsigned NOT NULL,
+ `talentLevel` tinyint(3) unsigned NOT NULL,
+ `duration` int(11) NOT NULL DEFAULT 0,
+ `powerType` smallint(6) NOT NULL,
+ `powerCost` smallint(5) unsigned NOT NULL,
+ `powerCostPerLevel` tinyint(3) unsigned NOT NULL,
+ `powerCostPercent` tinyint(3) unsigned NOT NULL,
+ `powerPerSecond` smallint(5) unsigned NOT NULL,
+ `powerPerSecondPerLevel` tinyint(3) unsigned NOT NULL,
+ `powerGainRunicPower` smallint(5) unsigned NOT NULL,
+ `powerCostRunes` smallint(5) unsigned NOT NULL,
+ `rangeId` smallint(5) unsigned NOT NULL,
+ `stackAmount` mediumint(8) unsigned NOT NULL,
+ `tool1` mediumint(8) unsigned NOT NULL,
+ `tool2` mediumint(8) unsigned NOT NULL,
+ `toolCategory1` tinyint(3) unsigned NOT NULL,
+ `toolCategory2` tinyint(3) unsigned NOT NULL,
+ `reagent1` mediumint(9) NOT NULL,
+ `reagent2` mediumint(9) NOT NULL,
+ `reagent3` mediumint(9) NOT NULL,
+ `reagent4` mediumint(9) NOT NULL,
+ `reagent5` mediumint(9) NOT NULL,
+ `reagent6` mediumint(9) NOT NULL,
+ `reagent7` mediumint(9) NOT NULL,
+ `reagent8` mediumint(9) NOT NULL,
+ `reagentCount1` tinyint(4) NOT NULL,
+ `reagentCount2` tinyint(4) NOT NULL,
+ `reagentCount3` tinyint(4) NOT NULL,
+ `reagentCount4` tinyint(4) NOT NULL,
+ `reagentCount5` tinyint(4) NOT NULL,
+ `reagentCount6` tinyint(4) NOT NULL,
+ `reagentCount7` tinyint(4) NOT NULL,
+ `reagentCount8` tinyint(4) NOT NULL,
+ `equippedItemClass` tinyint(4) NOT NULL,
+ `equippedItemSubClassMask` int(11) NOT NULL,
+ `equippedItemInventoryTypeMask` int(10) unsigned NOT NULL,
+ `effect1Id` smallint(5) unsigned NOT NULL,
+ `effect2Id` smallint(5) unsigned NOT NULL,
+ `effect3Id` smallint(5) unsigned NOT NULL,
+ `effect1DieSides` int(11) NOT NULL,
+ `effect2DieSides` int(11) NOT NULL,
+ `effect3DieSides` int(11) NOT NULL,
+ `effect1RealPointsPerLevel` float NOT NULL,
+ `effect2RealPointsPerLevel` float NOT NULL,
+ `effect3RealPointsPerLevel` float NOT NULL,
+ `effect1BasePoints` int(11) NOT NULL,
+ `effect2BasePoints` int(11) NOT NULL,
+ `effect3BasePoints` int(11) NOT NULL,
+ `effect1Mechanic` tinyint(3) unsigned NOT NULL,
+ `effect2Mechanic` tinyint(3) unsigned NOT NULL,
+ `effect3Mechanic` tinyint(3) unsigned NOT NULL,
+ `effect1ImplicitTargetA` smallint(6) NOT NULL,
+ `effect2ImplicitTargetA` smallint(6) NOT NULL,
+ `effect3ImplicitTargetA` smallint(6) NOT NULL,
+ `effect1ImplicitTargetB` smallint(6) NOT NULL,
+ `effect2ImplicitTargetB` smallint(6) NOT NULL,
+ `effect3ImplicitTargetB` smallint(6) NOT NULL,
+ `effect1RadiusMin` smallint(5) unsigned NOT NULL,
+ `effect1RadiusMax` smallint(5) unsigned NOT NULL DEFAULT 0,
+ `effect2RadiusMin` smallint(5) unsigned NOT NULL,
+ `effect2RadiusMax` smallint(5) unsigned NOT NULL DEFAULT 0,
+ `effect3RadiusMin` smallint(5) unsigned NOT NULL,
+ `effect3RadiusMax` smallint(5) unsigned NOT NULL DEFAULT 0,
+ `effect1AuraId` smallint(5) unsigned NOT NULL,
+ `effect2AuraId` smallint(5) unsigned NOT NULL,
+ `effect3AuraId` smallint(5) unsigned NOT NULL,
+ `effect1Periode` mediumint(8) unsigned NOT NULL,
+ `effect2Periode` mediumint(8) unsigned NOT NULL,
+ `effect3Periode` mediumint(8) unsigned NOT NULL,
+ `effect1ValueMultiplier` float NOT NULL,
+ `effect2ValueMultiplier` float NOT NULL,
+ `effect3ValueMultiplier` float NOT NULL,
+ `effect1ChainTarget` smallint(5) unsigned NOT NULL,
+ `effect2ChainTarget` smallint(5) unsigned NOT NULL,
+ `effect3ChainTarget` smallint(5) unsigned NOT NULL,
+ `effect1CreateItemId` int(11) NOT NULL,
+ `effect2CreateItemId` int(11) NOT NULL,
+ `effect3CreateItemId` int(11) NOT NULL,
+ `effect1MiscValue` int(11) NOT NULL,
+ `effect2MiscValue` int(11) NOT NULL,
+ `effect3MiscValue` int(11) NOT NULL,
+ `effect1MiscValueB` mediumint(9) NOT NULL,
+ `effect2MiscValueB` mediumint(9) NOT NULL,
+ `effect3MiscValueB` mediumint(9) NOT NULL,
+ `effect1TriggerSpell` mediumint(9) NOT NULL,
+ `effect2TriggerSpell` mediumint(9) NOT NULL,
+ `effect3TriggerSpell` mediumint(9) NOT NULL,
+ `effect1PointsPerComboPoint` mediumint(9) NOT NULL,
+ `effect2PointsPerComboPoint` mediumint(9) NOT NULL,
+ `effect3PointsPerComboPoint` mediumint(9) NOT NULL,
+ `effect1SpellClassMaskA` int(11) NOT NULL,
+ `effect1SpellClassMaskB` int(11) NOT NULL,
+ `effect1SpellClassMaskC` int(11) NOT NULL,
+ `effect2SpellClassMaskA` int(11) NOT NULL,
+ `effect2SpellClassMaskB` int(11) NOT NULL,
+ `effect2SpellClassMaskC` int(11) NOT NULL,
+ `effect3SpellClassMaskA` int(11) NOT NULL,
+ `effect3SpellClassMaskB` int(11) NOT NULL,
+ `effect3SpellClassMaskC` int(11) NOT NULL,
+ `effect1DamageMultiplier` float NOT NULL,
+ `effect2DamageMultiplier` float NOT NULL,
+ `effect3DamageMultiplier` float NOT NULL,
+ `effect1BonusMultiplier` float NOT NULL,
+ `effect2BonusMultiplier` float NOT NULL,
+ `effect3BonusMultiplier` float NOT NULL,
+ `iconId` smallint(5) unsigned NOT NULL DEFAULT 0,
+ `iconIdBak` smallint(5) unsigned NOT NULL DEFAULT 0,
+ `iconIdAlt` mediumint(8) unsigned NOT NULL DEFAULT 0,
+ `rankNo` tinyint(3) unsigned NOT NULL,
+ `spellVisualId` smallint(5) unsigned NOT NULL,
+ `name_loc0` varchar(115) DEFAULT NULL,
+ `name_loc2` varchar(115) DEFAULT NULL,
+ `name_loc3` varchar(115) DEFAULT NULL,
+ `name_loc4` varchar(115) DEFAULT NULL,
+ `name_loc6` varchar(115) DEFAULT NULL,
+ `name_loc8` varchar(184) DEFAULT NULL,
+ `rank_loc0` varchar(21) DEFAULT NULL,
+ `rank_loc2` varchar(25) DEFAULT NULL,
+ `rank_loc3` varchar(22) DEFAULT NULL,
+ `rank_loc4` varchar(21) DEFAULT NULL,
+ `rank_loc6` varchar(29) DEFAULT NULL,
+ `rank_loc8` varchar(56) DEFAULT NULL,
+ `description_loc0` text DEFAULT NULL,
+ `description_loc2` text DEFAULT NULL,
+ `description_loc3` text DEFAULT NULL,
+ `description_loc4` text DEFAULT NULL,
+ `description_loc6` text DEFAULT NULL,
+ `description_loc8` text DEFAULT NULL,
+ `buff_loc0` text DEFAULT NULL,
+ `buff_loc2` text DEFAULT NULL,
+ `buff_loc3` text DEFAULT NULL,
+ `buff_loc4` text DEFAULT NULL,
+ `buff_loc6` text DEFAULT NULL,
+ `buff_loc8` text DEFAULT NULL,
+ `maxTargetLevel` tinyint(3) unsigned NOT NULL,
+ `spellFamilyId` tinyint(3) unsigned NOT NULL,
+ `spellFamilyFlags1` int(11) NOT NULL,
+ `spellFamilyFlags2` int(11) NOT NULL,
+ `spellFamilyFlags3` int(11) NOT NULL,
+ `maxAffectedTargets` tinyint(3) unsigned NOT NULL,
+ `damageClass` tinyint(3) unsigned NOT NULL,
+ `skillLine1` smallint(6) NOT NULL DEFAULT 0,
+ `skillLine2OrMask` bigint(20) NOT NULL DEFAULT 0,
+ `reqRaceMask` smallint(5) unsigned NOT NULL DEFAULT 0,
+ `reqClassMask` smallint(5) unsigned NOT NULL DEFAULT 0,
+ `reqSpellId` mediumint(8) unsigned NOT NULL DEFAULT 0,
+ `reqSkillLevel` smallint(5) unsigned NOT NULL DEFAULT 0,
+ `learnedAt` smallint(5) unsigned NOT NULL DEFAULT 0,
+ `skillLevelGrey` smallint(5) unsigned NOT NULL DEFAULT 0,
+ `skillLevelYellow` smallint(5) unsigned NOT NULL DEFAULT 0,
+ `schoolMask` tinyint(3) unsigned NOT NULL,
+ `spellDescriptionVariableId` smallint(6) NOT NULL,
+ `trainingCost` int(10) unsigned NOT NULL,
+ PRIMARY KEY (`id`),
+ KEY `category` (`typeCat`),
+ KEY `spell` (`id`) USING BTREE,
+ KEY `iconId` (`iconId`),
+ KEY `reagent1` (`reagent1`),
+ KEY `reagent2` (`reagent2`),
+ KEY `reagent3` (`reagent3`),
+ KEY `reagent4` (`reagent4`),
+ KEY `reagent5` (`reagent5`),
+ KEY `reagent6` (`reagent6`),
+ KEY `reagent7` (`reagent7`),
+ KEY `reagent8` (`reagent8`),
+ KEY `effect1CreateItemId` (`effect1CreateItemId`),
+ KEY `effect2CreateItemId` (`effect2CreateItemId`),
+ KEY `effect3CreateItemId` (`effect3CreateItemId`),
+ KEY `effect1Id` (`effect1Id`),
+ KEY `effect2Id` (`effect2Id`),
+ KEY `effect3Id` (`effect3Id`),
+ KEY `effect1AuraId` (`effect1AuraId`),
+ KEY `effect2AuraId` (`effect2AuraId`),
+ KEY `effect3AuraId` (`effect3AuraId`),
+ KEY `idx_skill1` (`skillLine1`),
+ KEY `idx_skill2` (`skillLine2OrMask`),
+ KEY `idx_name0` (`name_loc0`),
+ KEY `idx_name2` (`name_loc2`),
+ KEY `idx_name3` (`name_loc3`),
+ KEY `idx_name4` (`name_loc4`),
+ KEY `idx_name6` (`name_loc6`),
+ KEY `idx_name8` (`name_loc8`),
+ KEY `idx_spellfamily` (`spellFamilyId`),
+ KEY `idx_miscvalue1` (`effect1MiscValue`),
+ KEY `idx_miscvalue2` (`effect2MiscValue`),
+ KEY `idx_miscvalue3` (`effect3MiscValue`),
+ KEY `idx_triggerspell1` (`effect1TriggerSpell`),
+ KEY `idx_triggerspell2` (`effect2TriggerSpell`),
+ KEY `idx_triggerspell3` (`effect3TriggerSpell`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Table structure for table `aowow_spell_search`
+--
+
+DROP TABLE IF EXISTS `aowow_spell_search`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `aowow_spell_search` (
+ `id` mediumint(8) unsigned NOT NULL,
+ `locale` tinyint(3) unsigned NOT NULL,
+ `nName` varchar(185) DEFAULT NULL,
+ `nDescription` text DEFAULT NULL,
+ `nBuff` text DEFAULT NULL,
+ PRIMARY KEY (`id`,`locale`),
+ FULLTEXT KEY `idx_ft_na` (`nName`),
+ FULLTEXT KEY `idx_ft_na_ex` (`nName`,`nDescription`,`nBuff`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Table structure for table `aowow_spell_sounds`
+--
+
+DROP TABLE IF EXISTS `aowow_spell_sounds`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `aowow_spell_sounds` (
+ `id` smallint(5) unsigned NOT NULL COMMENT 'SpellVisual.dbc/id',
+ `animation` smallint(5) unsigned NOT NULL DEFAULT 0,
+ `ready` smallint(5) unsigned NOT NULL DEFAULT 0,
+ `precast` smallint(5) unsigned NOT NULL DEFAULT 0,
+ `cast` smallint(5) unsigned NOT NULL DEFAULT 0,
+ `impact` smallint(5) unsigned NOT NULL DEFAULT 0,
+ `state` smallint(5) unsigned NOT NULL DEFAULT 0,
+ `statedone` smallint(5) unsigned NOT NULL DEFAULT 0,
+ `channel` smallint(5) unsigned NOT NULL DEFAULT 0,
+ `casterimpact` smallint(5) unsigned NOT NULL DEFAULT 0,
+ `targetimpact` smallint(5) unsigned NOT NULL DEFAULT 0,
+ `castertargeting` smallint(5) unsigned NOT NULL DEFAULT 0,
+ `missiletargeting` smallint(5) unsigned NOT NULL DEFAULT 0,
+ `instantarea` smallint(5) unsigned NOT NULL DEFAULT 0,
+ `persistentarea` smallint(5) unsigned NOT NULL DEFAULT 0,
+ `casterstate` smallint(5) unsigned NOT NULL DEFAULT 0,
+ `targetstate` smallint(5) unsigned NOT NULL DEFAULT 0,
+ `missile` smallint(5) unsigned NOT NULL DEFAULT 0 COMMENT 'not predicted by js',
+ `impactarea` smallint(5) unsigned NOT NULL DEFAULT 0 COMMENT 'not predicted by js',
+ PRIMARY KEY (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='!ATTENTION!\r\nthe primary key of this table is NOT a spellId, but spellVisualId\r\n\r\ncolumn names from LANG.sound_activities';
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Table structure for table `aowow_spelldifficulty`
+--
+
+DROP TABLE IF EXISTS `aowow_spelldifficulty`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `aowow_spelldifficulty` (
+ `normal10` mediumint(8) unsigned NOT NULL,
+ `normal25` mediumint(8) unsigned NOT NULL,
+ `heroic10` mediumint(8) unsigned NOT NULL,
+ `heroic25` mediumint(8) unsigned NOT NULL,
+ `mapType` tinyint(3) unsigned NOT NULL,
+ KEY `normal10` (`normal10`),
+ KEY `normal25` (`normal25`),
+ KEY `heroic10` (`heroic10`),
+ KEY `heroic25` (`heroic25`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Table structure for table `aowow_talents`
+--
+
+DROP TABLE IF EXISTS `aowow_talents`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `aowow_talents` (
+ `id` smallint(5) unsigned NOT NULL,
+ `class` tinyint(3) unsigned NOT NULL,
+ `petTypeMask` tinyint(3) unsigned NOT NULL,
+ `tab` tinyint(3) unsigned NOT NULL,
+ `row` tinyint(3) unsigned NOT NULL,
+ `col` tinyint(3) unsigned NOT NULL,
+ `spell` mediumint(8) unsigned NOT NULL,
+ `rank` tinyint(3) unsigned NOT NULL,
+ PRIMARY KEY (`id`,`rank`),
+ KEY `spell` (`spell`),
+ KEY `class` (`class`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Table structure for table `aowow_taxinodes`
+--
+
+DROP TABLE IF EXISTS `aowow_taxinodes`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `aowow_taxinodes` (
+ `id` smallint(5) unsigned NOT NULL,
+ `mapId` smallint(5) unsigned NOT NULL,
+ `mapX` float unsigned NOT NULL,
+ `mapY` float unsigned NOT NULL,
+ `areaId` smallint(5) unsigned NOT NULL,
+ `areaX` float unsigned NOT NULL,
+ `areaY` float unsigned NOT NULL,
+ `type` enum('NPC','GOBJECT') NOT NULL,
+ `typeId` mediumint(8) unsigned NOT NULL,
+ `reactA` tinyint(4) NOT NULL,
+ `reactH` tinyint(4) NOT NULL,
+ `name_loc0` varchar(59) DEFAULT NULL,
+ `name_loc2` varchar(84) DEFAULT NULL,
+ `name_loc3` varchar(61) DEFAULT NULL,
+ `name_loc4` varchar(59) DEFAULT NULL,
+ `name_loc6` varchar(89) DEFAULT NULL,
+ `name_loc8` varchar(142) DEFAULT NULL,
+ PRIMARY KEY (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Table structure for table `aowow_taxipath`
+--
+
+DROP TABLE IF EXISTS `aowow_taxipath`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `aowow_taxipath` (
+ `id` smallint(5) unsigned NOT NULL,
+ `startNodeId` smallint(5) unsigned NOT NULL,
+ `endNodeId` smallint(5) unsigned NOT NULL,
+ PRIMARY KEY (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Table structure for table `aowow_titles`
+--
+
+DROP TABLE IF EXISTS `aowow_titles`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `aowow_titles` (
+ `id` tinyint(3) unsigned NOT NULL,
+ `category` tinyint(3) unsigned NOT NULL,
+ `cuFlags` int(10) unsigned NOT NULL DEFAULT 0 COMMENT 'see defines.php for flags',
+ `gender` tinyint(3) unsigned NOT NULL,
+ `side` tinyint(3) unsigned NOT NULL,
+ `expansion` tinyint(3) unsigned NOT NULL,
+ `src12Ext` mediumint(8) unsigned NOT NULL,
+ `eventId` smallint(5) unsigned NOT NULL,
+ `bitIdx` tinyint(3) unsigned NOT NULL,
+ `male_loc0` varchar(33) DEFAULT NULL,
+ `male_loc2` varchar(35) DEFAULT NULL,
+ `male_loc3` varchar(37) DEFAULT NULL,
+ `male_loc4` varchar(37) DEFAULT NULL,
+ `male_loc6` varchar(34) DEFAULT NULL,
+ `male_loc8` varchar(37) DEFAULT NULL,
+ `female_loc0` varchar(33) DEFAULT NULL,
+ `female_loc2` varchar(35) DEFAULT NULL,
+ `female_loc3` varchar(39) DEFAULT NULL,
+ `female_loc4` varchar(39) DEFAULT NULL,
+ `female_loc6` varchar(35) DEFAULT NULL,
+ `female_loc8` varchar(41) DEFAULT NULL,
+ PRIMARY KEY (`id`),
+ KEY `bitIdx` (`bitIdx`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Table structure for table `aowow_user_ratings`
+--
+
+DROP TABLE IF EXISTS `aowow_user_ratings`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `aowow_user_ratings` (
+ `type` enum('COMMENT','GUIDE') NOT NULL,
+ `entry` int(11) NOT NULL DEFAULT 0,
+ `userId` int(10) unsigned DEFAULT NULL,
+ `value` tinyint(4) NOT NULL DEFAULT 0 COMMENT 'Rating Set',
+ UNIQUE KEY `type` (`type`,`entry`,`userId`),
+ KEY `FK_acc_co_rate_user` (`userId`),
+ CONSTRAINT `FK_userId` FOREIGN KEY (`userId`) REFERENCES `aowow_account` (`id`) ON DELETE SET NULL ON UPDATE CASCADE
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Table structure for table `aowow_videos`
+--
+
+DROP TABLE IF EXISTS `aowow_videos`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `aowow_videos` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `type` smallint(5) unsigned NOT NULL,
+ `typeId` mediumint(9) NOT NULL,
+ `userIdOwner` int(10) unsigned DEFAULT NULL,
+ `date` int(11) NOT NULL,
+ `videoId` varchar(12) NOT NULL,
+ `pos` tinyint(3) unsigned NOT NULL,
+ `url` varchar(64) NOT NULL COMMENT 'preview thumb',
+ `width` smallint(5) unsigned NOT NULL,
+ `height` smallint(5) unsigned NOT NULL,
+ `name` varchar(64) DEFAULT NULL,
+ `caption` varchar(200) DEFAULT NULL,
+ `status` int(11) NOT NULL,
+ `userIdApprove` int(10) unsigned DEFAULT NULL,
+ `userIdeDelete` int(10) unsigned DEFAULT NULL,
+ PRIMARY KEY (`id`),
+ KEY `type` (`type`,`typeId`),
+ KEY `FK_acc_vi` (`userIdOwner`),
+ CONSTRAINT `FK_acc_vi` FOREIGN KEY (`userIdOwner`) REFERENCES `aowow_account` (`id`) ON DELETE SET NULL ON UPDATE CASCADE
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=COMPACT;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Table structure for table `aowow_zones`
+--
+
+DROP TABLE IF EXISTS `aowow_zones`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `aowow_zones` (
+ `id` smallint(5) unsigned NOT NULL COMMENT 'Zone Id',
+ `mapId` smallint(5) unsigned NOT NULL COMMENT 'Map Identifier',
+ `mapIdBak` smallint(5) unsigned NOT NULL,
+ `parentArea` smallint(5) unsigned NOT NULL,
+ `category` smallint(5) unsigned NOT NULL,
+ `flags` int(10) unsigned NOT NULL,
+ `cuFlags` int(10) unsigned NOT NULL DEFAULT 0 COMMENT 'see defines.php for flags',
+ `faction` tinyint(3) unsigned NOT NULL,
+ `expansion` tinyint(3) unsigned NOT NULL,
+ `type` tinyint(3) unsigned NOT NULL,
+ `maxPlayer` tinyint(4) NOT NULL,
+ `itemLevelReqN` smallint(5) unsigned NOT NULL,
+ `itemLevelReqH` smallint(5) unsigned NOT NULL,
+ `levelReq` tinyint(3) unsigned NOT NULL,
+ `levelReqLFG` tinyint(3) unsigned NOT NULL,
+ `levelHeroic` tinyint(3) unsigned NOT NULL,
+ `levelMin` tinyint(3) unsigned NOT NULL,
+ `levelMax` tinyint(3) unsigned NOT NULL,
+ `attunementsN` text NOT NULL COMMENT 'space separated; type:typeId',
+ `attunementsH` text NOT NULL COMMENT 'space separated; type:typeId',
+ `parentMapId` smallint(5) unsigned NOT NULL,
+ `parentX` float NOT NULL,
+ `parentY` float NOT NULL,
+ `name_loc0` varchar(120) DEFAULT NULL COMMENT 'Map Name',
+ `name_loc2` varchar(120) DEFAULT NULL,
+ `name_loc3` varchar(120) DEFAULT NULL,
+ `name_loc4` varchar(120) DEFAULT NULL,
+ `name_loc6` varchar(120) DEFAULT NULL,
+ `name_loc8` varchar(120) DEFAULT NULL,
+ PRIMARY KEY (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Table structure for table `aowow_zones_sounds`
+--
+
+DROP TABLE IF EXISTS `aowow_zones_sounds`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `aowow_zones_sounds` (
+ `id` smallint(5) unsigned NOT NULL,
+ `ambienceDay` smallint(5) unsigned NOT NULL,
+ `ambienceNight` smallint(5) unsigned NOT NULL,
+ `musicDay` smallint(5) unsigned NOT NULL,
+ `musicNight` smallint(5) unsigned NOT NULL,
+ `intro` smallint(5) unsigned NOT NULL,
+ `worldStateId` smallint(5) unsigned NOT NULL,
+ `worldStateValue` smallint(6) NOT NULL,
+ KEY `id` (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+/*!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 */;
+/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;
+
+-- Dump completed on 2025-09-22 23:29:16
diff --git a/setup/sql/02-db_initial_data.sql b/setup/sql/02-db_initial_data.sql
new file mode 100644
index 00000000..fe750cf3
--- /dev/null
+++ b/setup/sql/02-db_initial_data.sql
@@ -0,0 +1,168 @@
+-- MariaDB dump 10.19 Distrib 10.4.32-MariaDB, for Win64 (AMD64)
+--
+-- Host: localhost Database: aowow
+-- ------------------------------------------------------
+-- Server version 10.4.32-MariaDB
+
+/*!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_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;
+/*!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' */;
+/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;
+
+--
+-- Dumping data for table `aowow_account`
+--
+
+LOCK TABLES `aowow_account` WRITE;
+/*!40000 ALTER TABLE `aowow_account` DISABLE KEYS */;
+INSERT INTO `aowow_account` VALUES (0,0,'','','AoWoW',NULL,0,0,0,'','',0,0,0,0,0,'','','',1,0,0,0,'',0,2,'',0);
+/*!40000 ALTER TABLE `aowow_account` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Dumping data for table `aowow_account_weightscales`
+--
+
+LOCK TABLES `aowow_account_weightscales` WRITE;
+/*!40000 ALTER TABLE `aowow_account_weightscales` DISABLE KEYS */;
+INSERT INTO `aowow_account_weightscales` VALUES (1,0,'arms',1,0,'ability_rogue_eviscerate'),(2,0,'fury',1,1,'ability_warrior_innerrage'),(3,0,'prot',1,2,'ability_warrior_defensivestance'),(4,0,'holy',2,0,'spell_holy_holybolt'),(5,0,'prot',2,1,'ability_paladin_shieldofthetemplar'),(6,0,'retrib',2,2,'spell_holy_auraoflight'),(7,0,'beast',3,0,'ability_hunter_beasttaming'),(8,0,'marks',3,1,'ability_marksmanship'),(9,0,'surv',3,2,'ability_hunter_swiftstrike'),(10,0,'assas',4,0,'ability_rogue_eviscerate'),(11,0,'combat',4,1,'ability_backstab'),(12,0,'subtle',4,2,'ability_stealth'),(13,0,'disc',5,0,'spell_holy_wordfortitude'),(14,0,'holy',5,1,'spell_holy_guardianspirit'),(15,0,'shadow',5,2,'spell_shadow_shadowwordpain'),(16,0,'blooddps',6,0,'spell_deathknight_bloodpresence'),(17,0,'frostdps',6,1,'spell_deathknight_frostpresence'),(18,0,'frosttank',6,2,'spell_deathknight_frostpresence'),(19,0,'unholydps',6,3,'spell_deathknight_unholypresence'),(20,0,'elem',7,0,'spell_nature_lightning'),(21,0,'enhance',7,1,'spell_nature_lightningshield'),(22,0,'resto',7,2,'spell_nature_magicimmunity'),(23,0,'arcane',8,0,'spell_holy_magicalsentry'),(24,0,'fire',8,1,'spell_fire_firebolt02'),(25,0,'frost',8,2,'spell_frost_frostbolt02'),(26,0,'afflic',9,0,'spell_shadow_deathcoil'),(27,0,'demo',9,1,'spell_shadow_metamorphosis'),(28,0,'destro',9,2,'spell_shadow_rainoffire'),(29,0,'balance',11,0,'spell_nature_starfall'),(30,0,'feraltank',11,2,'ability_racial_bearform'),(31,0,'resto',11,3,'spell_nature_healingtouch'),(32,0,'feraldps',11,1,'ability_druid_catform');
+/*!40000 ALTER TABLE `aowow_account_weightscales` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Dumping data for table `aowow_account_weightscale_data`
+--
+
+LOCK TABLES `aowow_account_weightscale_data` WRITE;
+/*!40000 ALTER TABLE `aowow_account_weightscale_data` DISABLE KEYS */;
+INSERT INTO `aowow_account_weightscale_data` VALUES (2,'exprtng',100),(2,'str',82),(2,'critstrkrtng',66),(2,'agi',53),(2,'armorpenrtng',52),(2,'hitrtng',48),(2,'hastertng',36),(2,'atkpwr',31),(2,'armor',5),(3,'sta',100),(3,'dodgertng',90),(3,'defrtng',86),(3,'block',81),(3,'agi',67),(3,'parryrtng',67),(3,'blockrtng',48),(3,'str',48),(3,'exprtng',19),(3,'hitrtng',10),(3,'armorpenrtng',10),(3,'critstrkrtng',7),(3,'armor',6),(3,'hastertng',1),(3,'atkpwr',1),(4,'int',100),(4,'manargn',88),(4,'splpwr',58),(4,'critstrkrtng',46),(4,'hastertng',35),(5,'sta',100),(5,'dodgertng',94),(5,'block',86),(5,'defrtng',86),(5,'exprtng',79),(5,'agi',76),(5,'parryrtng',76),(5,'hitrtng',58),(5,'blockrtng',52),(5,'str',50),(5,'armor',6),(5,'atkpwr',6),(5,'splpwr',4),(5,'critstrkrtng',3),(6,'mledps',470),(6,'hitrtng',100),(6,'str',80),(6,'exprtng',66),(6,'critstrkrtng',40),(6,'atkpwr',34),(6,'agi',32),(6,'hastertng',30),(6,'armorpenrtng',22),(6,'splpwr',9),(7,'rgddps',213),(7,'hitrtng',100),(7,'agi',58),(7,'critstrkrtng',40),(7,'int',37),(7,'atkpwr',30),(7,'armorpenrtng',28),(7,'hastertng',21),(8,'rgddps',379),(8,'hitrtng',100),(8,'agi',74),(8,'critstrkrtng',57),(8,'armorpenrtng',40),(8,'int',39),(8,'atkpwr',32),(8,'hastertng',24),(9,'rgddps',181),(9,'hitrtng',100),(9,'agi',76),(9,'critstrkrtng',42),(9,'int',35),(9,'hastertng',31),(9,'atkpwr',29),(9,'armorpenrtng',26),(10,'mledps',170),(10,'agi',100),(10,'exprtng',87),(10,'hitrtng',83),(10,'critstrkrtng',81),(10,'atkpwr',65),(10,'armorpenrtng',65),(10,'hastertng',64),(10,'str',55),(11,'mledps',220),(11,'armorpenrtng',100),(11,'agi',100),(11,'exprtng',82),(11,'hitrtng',80),(11,'critstrkrtng',75),(11,'hastertng',73),(11,'str',55),(11,'atkpwr',50),(12,'mledps',228),(12,'exprtng',100),(12,'agi',100),(12,'hitrtng',80),(12,'armorpenrtng',75),(12,'critstrkrtng',75),(12,'hastertng',75),(12,'str',55),(12,'atkpwr',50),(13,'splpwr',100),(13,'manargn',67),(13,'int',65),(13,'hastertng',59),(13,'critstrkrtng',48),(13,'spi',22),(14,'manargn',100),(14,'int',69),(14,'splpwr',60),(14,'spi',52),(14,'critstrkrtng',38),(14,'hastertng',31),(15,'hitrtng',100),(15,'shasplpwr',76),(15,'splpwr',76),(15,'critstrkrtng',54),(15,'hastertng',50),(15,'spi',16),(15,'int',16),(16,'mledps',360),(16,'armorpenrtng',100),(16,'str',99),(16,'hitrtng',91),(16,'exprtng',90),(16,'critstrkrtng',57),(16,'hastertng',55),(16,'atkpwr',36),(16,'armor',1),(17,'mledps',337),(17,'hitrtng',100),(17,'str',97),(17,'exprtng',81),(17,'armorpenrtng',61),(17,'critstrkrtng',45),(17,'atkpwr',35),(17,'hastertng',28),(17,'armor',1),(18,'mledps',419),(18,'parryrtng',100),(18,'hitrtng',97),(18,'str',96),(18,'defrtng',85),(18,'exprtng',69),(18,'dodgertng',61),(18,'agi',61),(18,'sta',61),(18,'critstrkrtng',49),(18,'atkpwr',41),(18,'armorpenrtng',31),(18,'armor',5),(19,'mledps',209),(19,'str',100),(19,'hitrtng',66),(19,'exprtng',51),(19,'hastertng',48),(19,'critstrkrtng',45),(19,'atkpwr',34),(19,'armorpenrtng',32),(19,'armor',1),(20,'hitrtng',100),(20,'splpwr',60),(20,'hastertng',56),(20,'critstrkrtng',40),(20,'int',11),(21,'mledps',135),(21,'hitrtng',100),(21,'exprtng',84),(21,'agi',55),(21,'int',55),(21,'critstrkrtng',55),(21,'hastertng',42),(21,'str',35),(21,'atkpwr',32),(21,'splpwr',29),(21,'armorpenrtng',26),(22,'manargn',100),(22,'int',85),(22,'splpwr',77),(22,'critstrkrtng',62),(22,'hastertng',35),(23,'hitrtng',100),(23,'hastertng',54),(23,'arcsplpwr',49),(23,'splpwr',49),(23,'critstrkrtng',37),(23,'int',34),(23,'frosplpwr',24),(23,'firsplpwr',24),(23,'spi',14),(24,'hitrtng',100),(24,'hastertng',53),(24,'firsplpwr',46),(24,'splpwr',46),(24,'critstrkrtng',43),(24,'frosplpwr',23),(24,'arcsplpwr',23),(24,'int',13),(25,'hitrtng',100),(25,'hastertng',42),(25,'frosplpwr',39),(25,'splpwr',39),(25,'arcsplpwr',19),(25,'firsplpwr',19),(25,'critstrkrtng',19),(25,'int',6),(26,'hitrtng',100),(26,'shasplpwr',72),(26,'splpwr',72),(26,'hastertng',61),(26,'critstrkrtng',38),(26,'firsplpwr',36),(26,'spi',34),(26,'int',15),(27,'hitrtng',100),(27,'hastertng',50),(27,'firsplpwr',45),(27,'shasplpwr',45),(27,'splpwr',45),(27,'critstrkrtng',31),(27,'spi',29),(27,'int',13),(28,'hitrtng',100),(28,'firsplpwr',47),(28,'splpwr',47),(28,'hastertng',46),(28,'spi',26),(28,'shasplpwr',23),(28,'critstrkrtng',16),(28,'int',13),(29,'hitrtng',100),(29,'splpwr',66),(29,'hastertng',54),(29,'critstrkrtng',43),(29,'spi',22),(29,'int',22),(30,'agi',100),(30,'sta',75),(30,'dodgertng',65),(30,'defrtng',60),(30,'exprtng',16),(30,'str',10),(30,'armor',10),(30,'hitrtng',8),(30,'hastertng',5),(30,'atkpwr',4),(30,'feratkpwr',4),(30,'critstrkrtng',3),(31,'splpwr',100),(31,'manargn',73),(31,'hastertng',57),(31,'int',51),(31,'spi',32),(31,'critstrkrtng',11),(32,'agi',100),(32,'armorpenrtng',90),(32,'str',80),(32,'critstrkrtng',55),(32,'exprtng',50),(32,'hitrtng',50),(32,'feratkpwr',40),(32,'atkpwr',40),(32,'hastertng',35);
+/*!40000 ALTER TABLE `aowow_account_weightscale_data` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Dumping data for table `aowow_announcements`
+--
+
+LOCK TABLES `aowow_announcements` WRITE;
+/*!40000 ALTER TABLE `aowow_announcements` DISABLE KEYS */;
+INSERT INTO `aowow_announcements` VALUES (4,'compare','Help: Item Comparison Tool',0,'padding-left: 55px; background-image: url(STATIC_URL/images/announcements/help-small.png); background-position: 10px center',1,1,'First time? - Don\'t be shy! Just check out our [url=?help=item-comparison]Help page[/url]!','Première visite? - Ne soyez pas intimidé! Vous n\'avez qu\'à lire notre [url=?help=item-comparison]page d\'aide[/url] !','Euer erstes Mal? Nur keine falsche Scheu! Schaut einfach auf unsere [url=?help=item-comparison]Hilfeseite[/url]!','','¿Tu primera vez? ¡No seas vergonzoso! !Mira nuestra [url=?help=item-comparison]página de ayuda[/url]!','Впервые? Не стесняйтесь посетить нашу [url=?help=item-comparison]справочную страницу[/url]!'),(3,'profile','Help: Profiler',0,'padding-left: 80px; background-image: url(STATIC_URL/images/announcements/help-large.gif); background-position: 10px center',1,1,'[h3]First Time?[/h3]\n\nThe [b]Profiler[/b] tool lets you [span class=tip title=\"e.g. See how\'d you look as a different race, try different gear or talents, and more!\"]edit your character[/span], find gear upgrades, check your gear score, and more!\n\n[ul]\n[li][b]Right-click[/b] slots to change items, add gems/enchants, or find upgrades.[/li]\n[li]Use the [b]Claim character[/b] button to add your own characters to your [url=?user]user page[/url].[/li]\n[li]Save a modified character to your Aowow account by using the [b]Save as[/b] button.[/li]\n[li][b]Statistics[/b] will update in real time as you make tweaks.[/li]\n[/ul]\n\nFor more information, check out our extensive [url=?help=profiler]help page[/url]!','','[h3]Euer erster Besuch?[/h3]\n\nDas [b]Profiler[/b]-Werkzeug erlaubt es euch [span class=tip title=\"z.B. Seht, wie Ihr als anderes Volk aussehen würdet, probiert andere Ausrüstung oder Talente aus, und mehr!\"]euren Charakter zu bearbeiten[/span], besser Ausrüstung zu finden, eure Ausrüstungswertung zu vergleichen, und vieles mehr!\n\n[ul]\n[li][b]Rechts-klickt[/b] Plätze um Gegenstände zu tauschen, Edelsteine/Verzauberungen hinzuzufügen, oder bessere AUsrüstung zu finden.[/li]\n[li]Benutzt [b]Charakter beanspruchen[/b] um eure eigenen Charaktere Eurer [url=?user]Benutzerseite[/url] hinzuzufügen.[/li]\n[li]Speichert einen modifizierten Charakter in Eurem Aowow-Konto, indem Ihr [b]Speichern als[/b] benutzt.[/li]\n[li]Die [b]Statistiken[/b] aktualisieren sich in Echtzeit, während Ihr Änderungen durchführt.[/li]\n[/ul]\n\nWeitere Informationen findet Ihr auf unserer umfangreichen [url=?help=profiler]Hilfeseite[/url]!','','',''),(2,'profiler','Help: Profiler',0,'padding-left: 80px; background-image: url(STATIC_URL/images/announcements/help-large.gif); background-position: 10px center',1,1,'[h3]First Time?[/h3]\n\nThe [b]Profiler[/b] tool lets you [span class=tip title=\"e.g. See how\'d you look as a different race, try different gear or talents, and more!\"]edit your character[/span], find gear upgrades, check your gear score, and more!\n\n[ul]\n[li][b]Right-click[/b] slots to change items, add gems/enchants, or find upgrades.[/li]\n[li]Use the [b]Claim character[/b] button to add your own characters to your [url=?user]user page[/url].[/li]\n[li]Save a modified character to your Aowow account by using the [b]Save as[/b] button.[/li]\n[li][b]Statistics[/b] will update in real time as you make tweaks.[/li]\n[/ul]\n\nFor more information, check out our extensive [url=?help=profiler]help page[/url]!','','[h3]Euer erster Besuch?[/h3]\n\nDas [b]Profiler[/b]-Werkzeug erlaubt es euch [span class=tip title=\"z.B. Seht, wie Ihr als anderes Volk aussehen würdet, probiert andere Ausrüstung oder Talente aus, und mehr!\"]euren Charakter zu bearbeiten[/span], besser Ausrüstung zu finden, eure Ausrüstungswertung zu vergleichen, und vieles mehr!\n\n[ul]\n[li][b]Rechts-klickt[/b] Plätze um Gegenstände zu tauschen, Edelsteine/Verzauberungen hinzuzufügen, oder bessere AUsrüstung zu finden.[/li]\n[li]Benutzt [b]Charakter beanspruchen[/b] um eure eigenen Charaktere Eurer [url=?user]Benutzerseite[/url] hinzuzufügen.[/li]\n[li]Speichert einen modifizierten Charakter in Eurem Aowow-Konto, indem Ihr [b]Speichern als[/b] benutzt.[/li]\n[li]Die [b]Statistiken[/b] aktualisieren sich in Echtzeit, während Ihr Änderungen durchführt.[/li]\n[/ul]\n\nWeitere Informationen findet Ihr auf unserer umfangreichen [url=?help=profiler]Hilfeseite[/url]!','','','');
+/*!40000 ALTER TABLE `aowow_announcements` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Dumping data for table `aowow_config`
+--
+
+LOCK TABLES `aowow_config` WRITE;
+/*!40000 ALTER TABLE `aowow_config` DISABLE KEYS */;
+INSERT INTO `aowow_config` VALUES ('logographic_ft_search','0','0',1,1156,'enables fulltext search for logographic languages (CN, KR, TW). The database MUST support this (i.e. MySQL implements ngram)'),('acc_allow_register','1','1',3,132,'allow/disallow account creation (requires AUTH_MODE: aowow)'),('acc_auth_mode','0','0',3,1425,'source to auth against - 0:AoWoW, 1:TC auth-table, 2:External script (config/extAuth.php)'),('acc_create_save_decay','604800','604800',3,129,'time in wich an unconfirmed account cannot be overwritten by new registrations'),('acc_ext_create_url','',NULL,3,136,'if auth mode is not self; link to external account creation'),('acc_ext_recover_url','',NULL,3,136,'if auth mode is not self; link to external account recovery'),('acc_failed_auth_block','900','15 * 60',3,129,'how long an account is closed after exceeding FAILED_AUTH_COUNT (in seconds)'),('acc_failed_auth_count','5','5',3,129,'how often invalid passwords are tolerated'),('acc_max_avatar_uploads','10','10',3,129,'premium users may upload this many avatars'),('acc_recovery_decay','300','300',3,129,'time to recover your account and new recovery requests are blocked'),('acc_rename_decay','2592000','30 * 24 * 60 * 60',3,129,'delay between username changes'),('battlegroup','Pure Pwnage',NULL,1,136,'pretend, we belong to a battlegroup to satisfy profiler-related javascripts'),('board_url','http://www.wowhead.com/forums?board=',NULL,1,136,'another halfbaked javascript thing..'),('cache_decay','25200','60 * 60 * 7',2,129,'time to keep cache in seconds'),('cache_dir','','cache/template',2,136,'generated pages are saved here (requires CACHE_MODE: filecache)'),('cache_mode','1','1',2,1185,'set cache method - 0:filecache, 1:memcached'),('contact_email','feedback@aowow.org',NULL,1,136,'displayed sender for auth-mails, ect'),('debug','0','0',1,145,'disable cache, enable error_reporting - 0:None, 1:Error, 2:Warning, 3:Info'),('default_charset','utf-8',NULL,0,72,''),('force_ssl','0','0',1,132,'enforce SSL, if auto-detect fails'),('gtag_measurement_id','',NULL,6,136,'enter your Google Tag measurement ID here to track site stats'),('locales','349','0x15D',1,1441,'allowed locales - 0:English, 2:French, 3:German, 4:Chinese, 6:Spanish, 8:Russian'),('maintenance','1','0',1,132,'display brb gnomes and block access for non-staff'),('memory_limit','1500M','1500M',0,200,'parsing spell.dbc is quite intense'),('name','Aowow Database Viewer (ADV)',NULL,1,136,'website title'),('name_short','Aowow',NULL,1,136,'feed title'),('profiler_enable','0','0',7,1412,'enable/disable profiler feature'),('profiler_queue_delay','3000','3000',7,129,'min. delay between queue cycles (in ms)'),('profiler_resync_delay','3600','1 * 60 * 60',7,129,'how often a character can be refreshed (in sec)'),('profiler_resync_ping','5000','5000',7,129,'how often the javascript asks for for updates, when queued (in ms)'),('rep_req_border_epic','15000','15000',5,129,'required reputation for epic quality avatar border'),('rep_req_border_legendary','25000','25000',5,129,'required reputation for legendary quality avatar border'),('rep_req_border_rare','10000','10000',5,129,'required reputation for rare quality avatar border'),('rep_req_border_uncommon','5000','5000',5,129,'required reputation for uncommon quality avatar border'),('rep_req_comment','75','75',5,129,'required reputation to write a comment'),('rep_req_downvote','250','250',5,129,'required reputation to downvote comments'),('rep_req_ext_links','150','150',5,129,'required reputation to link to external sites'),('rep_req_premium','25000','25000',5,129,'required reputation for premium status through reputation'),('rep_req_reply','75','75',5,129,'required reputation to write a reply'),('rep_req_supervote','2500','2500',5,129,'required reputation for double vote effect'),('rep_req_upvote','125','125',5,129,'required reputation to upvote comments'),('rep_req_votemore_add','250','250',5,129,'required reputation per additional vote past threshold'),('rep_req_votemore_base','2000','2000',5,129,'gains more votes past this threshold'),('rep_reward_article','100','100',5,129,'submitted an approved article/guide'),('rep_reward_bad_report','0','0',5,129,'filed a rejected report'),('rep_reward_comment','1','1',5,129,'created a comment (not a reply)'),('rep_reward_dailyvisit','5','5',5,129,'daily visit'),('rep_reward_downvoted','0','0',5,129,'comment received downvote'),('rep_reward_good_report','10','10',5,129,'filed an accepted report'),('rep_reward_register','100','100',5,129,'activated an account'),('rep_reward_submit_screenshot','10','10',5,129,'uploaded screenshot was approved'),('rep_reward_suggest_video','10','10',5,129,'suggested video was approved'),('rep_reward_upvoted','5','5',5,129,'comment received upvote'),('rep_reward_user_suspended','-200','-200',5,129,'moderator revoked rights'),('rep_reward_user_warned','-50','-50',5,129,'moderator imposed a warning'),('screenshot_min_size','200','200',1,1153,'minimum dimensions of uploaded screenshots in px (yes, it\'s square, no it cant go below 200)'),('serialize_precision','5',NULL,0,65,''),('session_cache_dir','',NULL,4,136,'php sessions are saved here. Leave empty to use php default directory.'),('session_timeout_delay','3600','60 * 60',4,129,'non-permanent session times out in time() + X'),('session.gc_divisor','100','100',4,200,'probability to remove session data on garbage collection'),('session.gc_maxlifetime','604800','7 * 24 * 60 * 60',4,200,'lifetime of session data'),('session.gc_probability','1','0',4,200,'probability to remove session data on garbage collection'),('site_host','',NULL,1,904,'points js to executable files'),('static_host','',NULL,1,904,'points js to images & scripts'),('ttl_rss','60','60',1,129,'time to live for RSS (in seconds)'),('ua_measurement_key','',NULL,6,136,'[DEPRECATED ?] Enter your Google Universal Analytics key here to track site stats'),('user_max_votes','50','50',1,129,'vote limit per day');
+/*!40000 ALTER TABLE `aowow_config` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Dumping data for table `aowow_dbversion`
+--
+
+LOCK TABLES `aowow_dbversion` WRITE;
+/*!40000 ALTER TABLE `aowow_dbversion` DISABLE KEYS */;
+INSERT INTO `aowow_dbversion` VALUES (1774551740,0,NULL,NULL);
+/*!40000 ALTER TABLE `aowow_dbversion` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Dumping data for table `aowow_home_featuredbox`
+--
+
+LOCK TABLES `aowow_home_featuredbox` WRITE;
+/*!40000 ALTER TABLE `aowow_home_featuredbox` DISABLE KEYS */;
+INSERT INTO `aowow_home_featuredbox` VALUES (1,NULL,0,0,0,0,'',NULL,NULL,'[pad]Welcome to [b][span class=q5]AoWoW[/span][/b]!','[pad]Bienvenue à [b][span class=q5]AoWoW[/span][/b]!','[pad]Willkommen bei [b][span class=q5]AoWoW[/span][/b]!','','','Добро[pad] пожаловать на [b][span class=q5]AoWoW[/span][/b]!'),(2,NULL,0,0,0,1,'STATIC_URL/images/logos/newsbox-explained.png',NULL,NULL,'[ul]\n[li][i]just demoing the newsbox here..[/i][/li]\n[li][b][url=http://www.example.com]..with urls[/url][/b][/li]\n[li][b]..typeLinks [item=45533][/b][/li]\n[li][b]..also, over there to the right is an overlay-trigger =>[/b][/li]\n[/ul]\n\n[ul]\n[li][tooltip name=demotip]hey, it hints you stuff![/tooltip][b][span class=tip tooltip=demotip]..hover me[/span][/b][/li]\n[/ul]','','','','','');
+/*!40000 ALTER TABLE `aowow_home_featuredbox` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Dumping data for table `aowow_home_featuredbox_overlay`
+--
+
+LOCK TABLES `aowow_home_featuredbox_overlay` WRITE;
+/*!40000 ALTER TABLE `aowow_home_featuredbox_overlay` DISABLE KEYS */;
+INSERT INTO `aowow_home_featuredbox_overlay` VALUES (2,405,100,'http://example.com','example overlay','','','','','');
+/*!40000 ALTER TABLE `aowow_home_featuredbox_overlay` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Dumping data for table `aowow_home_titles`
+--
+
+LOCK TABLES `aowow_home_titles` WRITE;
+/*!40000 ALTER TABLE `aowow_home_titles` DISABLE KEYS */;
+INSERT INTO `aowow_home_titles` VALUES (1,0,1522321542,1,0,'That\'s a 50 DKP plus!'),(2,0,1522321542,1,0,'We\'ve got what you need!'),(3,0,1522321542,1,0,'You haven\'t found the secret title yet.'),(4,0,1522321542,1,0,'...and knowing is half the battle!'),(5,0,1522321542,1,0,'Good news, everyone!'),(6,0,1522321542,1,0,'+1, Insightful'),(7,0,1522321542,1,0,'More effective than a [Booterang].'),(8,0,1522321542,1,0,'There is no cow level.'),(9,0,1522321542,1,0,'We\'ve got more style than a fashion designer who knows CSS.'),(10,0,1522321542,1,3,'Eure Fertigkeit in WoW hat sich auf 450 erhöht.'),(11,0,1522321542,1,0,'If you use your mouse to search, you won\'t be able to click on Rend.'),(12,0,1522321542,1,2,'Tout est dans l\'élégance.'),(13,0,1522321542,1,2,'Rend les chargements supportables depuis 2006.'),(14,0,1522321542,1,2,'Vous allez revenir.'),(15,0,1522321542,1,2,'Base de données extraordinaire'),(16,0,1522321542,1,2,'Si vous lisez ceci, arrêtez d\'appuyer sur F5.'),(17,0,1522321542,1,3,'Und der Tag ist gerettet.'),(18,0,1522321542,1,3,'Jetzt in allen bekannten Internetzen verfügbar!'),(19,0,1522321542,1,3,'Morgens, halb drei in Nordend'),(20,0,1522321542,1,3,'Macht auch Euren Webbrowser glücklich!'),(21,0,1522321542,1,3,'Hier findet Ihr sogar Mankriks Frau.'),(22,0,1522321542,1,6,'Base de datos extraordinaria de WoW'),(23,0,1522321542,1,6,'La única cosa en la que los ninjas y los piratas estan de acuerdo.'),(24,0,1522321542,1,6,'La elegancia lo es todo.'),(25,0,1522321542,1,6,'Hace feliz a los navegadores.'),(26,0,1522321542,1,8,'Ты ещё вернёшься.'),(27,0,1522321542,1,8,'Осваивание нового босса - 45 золота на ремонт. Персональный эпический предмет - 650 золотых'),(28,0,1522321542,1,8,'Не именной. Поделитесь им с друзьями!'),(29,0,1522321542,1,8,'Если вы здесь впервые, то вам необходимо воспользоваться поиском!'),(30,0,1522321542,1,8,'Приколы Мулгора без чата в Мулгоре.'),(31,0,1522321542,1,2,'Les trois premières lettres veulent tout dire.'),(32,0,1522321542,1,2,'Trouvez la femme de Mankrik grâce à lui.'),(33,0,1522321542,1,6,'Tu habilidad con WoW se ha incrementado a 450.'),(34,0,1522321542,1,6,'Buscando uno más: Tú'),(35,0,1522321542,1,8,'Первые три буквы говорят сами за себя.'),(36,0,1522321542,1,8,'У нас больше стиля, чем у дизайнера, знающего CSS.'),(37,0,1522321542,1,0,'Preventing wipes since 2006.'),(38,0,1522321542,1,0,'Never gonna give you up. Never gonna let you down.'),(39,0,1522321542,1,0,'The closest thing to an F1 key for WoW.'),(40,0,1522321542,1,2,'Non lié. Partagez-le avec vos amis !'),(41,0,1522321542,1,2,'Votre navigateur l\'adore !'),(42,0,1522321542,1,3,'Verhindert Wipes seit 2006.'),(43,0,1522321542,1,6,'+1, Utilidad'),(44,0,1522321542,1,6,'Épico, como tu líder de facción.'),(45,0,1522321542,1,8,'Он такой один...'),(46,0,1522321542,1,8,'Если вы это читаете, то прекратите обновлять страницу.'),(47,0,1522321542,1,0,'If you are reading this, stop pressing F5.'),(48,0,1522321542,1,2,'Chasse les jours pluvieux.'),(49,0,1522321542,1,3,'+1, Hilfreich'),(50,0,1522321542,1,3,'Episch - markant - dreifach verzaubert'),(51,0,1522321542,1,8,'Работает как положено.'),(52,0,1522321542,1,0,'Flagged for awesome.'),(53,0,1522321542,1,0,'Thrall-tested, Jaina-approved.'),(54,0,1522321542,1,8,'Всё дело в элегантности.'),(55,0,1522321542,1,0,'What does it mean?'),(56,0,1522321542,1,0,'YOU ARE NOW PREPARED!'),(57,0,1522321542,1,0,'srsly'),(58,0,1522321542,1,2,'C\'est comme prétendre être malade et aller à la plage, mais pour les bases de données.'),(59,0,1522321542,1,3,'Thrall-getestet, Jaina-genehmigt'),(60,0,1522321542,1,6,'Haciendo las pantallas de carga más soportables desde el 2006'),(61,0,1522321542,1,8,'Создан быть лидером.'),(62,0,1522321542,1,0,'You\'ll say \"Wow\" every time.'),(63,0,1522321542,1,0,'Dataz! We need more dataz!'),(64,0,1522321542,1,0,'Your skill in WoW has increased to 450.'),(65,0,1522321542,1,3,'Eleganz ist alles.'),(66,0,1522321542,1,8,'+1, Полезный'),(67,0,1522321542,1,8,'Ух ты!'),(68,0,1522321542,1,0,'Sometimes there is fire. You need to not be in it.'),(69,0,1522321542,1,0,'Working as intended.'),(70,0,1522321542,1,2,'La seule chose sur laquelle les ninjas et les pirates sont d\'accord.'),(71,0,1522321542,1,3,'Nicht seelengebunden. Teilt es mit Euren Freunden!'),(72,0,1522321542,1,8,'Теперь доступен во всех известных Интернетах!'),(73,0,1522321542,1,8,'Вы получаете добычу: [Легендарное Знание]'),(74,0,1522321542,1,0,'You\'ll be back.'),(75,0,1522321542,1,0,'Epic like your faction leader.'),(76,0,1522321542,1,3,'Manchmal gibt es Feuer. Ihr dürft nicht drin stehen.'),(77,0,1522321542,1,3,'Wer das hier lesen kann, drückt zu oft F5.'),(78,0,1522321542,1,6,'¡Datos! ¡Más Datos!'),(79,0,1522321542,1,8,'НЯМ НЯМ НЯМ'),(80,0,1522321542,1,2,'Testé par Thrall, approuvé par Jaina.'),(81,0,1522321542,1,8,'Сделайте его вашей новой расовой возможностью уже сегодня!'),(82,0,1522321542,1,0,'We do math, so you don\'t have to.'),(83,0,1522321542,1,0,'OM NOM NOM'),(84,0,1522321542,1,0,'Now available on all known internets!'),(85,0,1522321542,1,0,'We brake for dataz.'),(86,0,1522321542,1,3,'Neues von der Obstverkäuferfront'),(87,0,1522321542,1,6,'Las primeras tres palabras lo dicen todo.'),(88,0,1522321542,1,8,'Это как будто сказать всем, что ты болен, а самому пойти на пляж, - только для баз данных.'),(89,0,1522321542,1,8,'Меняем семечки на данные!'),(90,0,1522321542,1,0,'It\'s all about elegance.'),(91,0,1522321542,1,0,'Never underestimate the power of the Scout\'s code.'),(92,0,1522321542,1,6,'Elimina los días lluviosos.'),(93,0,1522321542,1,0,'You just won the game.'),(94,0,1522321542,1,8,'Данные! Нам надо больше данных!'),(95,0,1522321542,1,0,'WoW Database Extraordinaire'),(96,0,1522321542,1,0,'No longer soulbound. Can now be shared with friends!'),(97,0,1522321542,1,0,'The dataz you could be using.'),(98,0,1522321542,1,8,'Превосходен, как лидер вашей фракции.'),(99,0,1522321542,1,6,'¡Regresarás!');
+/*!40000 ALTER TABLE `aowow_home_titles` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Dumping data for table `aowow_loot_link`
+--
+
+LOCK TABLES `aowow_loot_link` WRITE;
+/*!40000 ALTER TABLE `aowow_loot_link` DISABLE KEYS */;
+INSERT INTO `aowow_loot_link` VALUES (19710,184465,1,0,0),(19218,184465,1,1,0),(21526,184849,2,0,0),(21525,184849,2,1,0),(17537,185168,1,1,0),(17536,185168,1,0,0),(18434,185169,2,1,0),(18432,185169,2,0,0),(28234,190586,1,0,0),(28234,193996,2,0,0),(26533,190663,1,0,0),(31217,193597,2,0,0),(27656,191349,1,0,0),(31561,193603,2,0,0),(28859,193905,1,0,0),(31734,193967,2,0,0),(32845,194307,1,0,0),(32846,194308,2,0,0),(32845,194200,3,0,0),(32846,194201,4,0,0),(32865,194312,1,0,0),(33147,194314,2,0,0),(32865,194313,3,0,0),(33147,194315,4,0,0),(32906,194324,1,0,0),(33360,194328,2,0,0),(32906,194327,3,0,0),(33360,194331,4,0,0),(32871,194821,1,0,0),(33070,194822,2,0,0),(33350,194789,1,0,0),(33350,194956,2,0,0),(33350,194957,3,0,0),(33350,194958,4,0,0),(32930,195046,1,0,0),(33909,195047,2,0,0),(34928,195323,1,0,0),(35517,195324,2,0,0),(35119,195374,1,0,0),(35518,195375,2,0,0),(37226,201710,1,0,0),(37226,202336,2,0,0),(36789,201959,1,0,0),(38174,202339,2,0,0),(36789,202338,3,0,0),(38174,202340,4,0,0),(38402,202239,1,0,0),(38582,202240,2,0,0),(37813,202238,3,0,0),(38583,202241,4,0,0),(9034,169243,1,0,243),(9035,169243,1,1,243),(9039,169243,1,0,243),(9036,169243,1,0,243),(9037,169243,1,0,243),(9038,169243,1,0,243),(9040,169243,1,0,243),(34657,195709,1,0,334),(34701,195709,1,0,334),(34703,195709,1,0,334),(34702,195709,1,0,334),(34705,195709,1,0,334),(35571,195709,1,0,334),(35617,195709,1,0,334),(35572,195709,1,0,334),(35570,195709,1,0,334),(35569,195709,1,1,334),(36089,195710,2,0,334),(36086,195710,2,0,334),(36087,195710,2,0,334),(36082,195710,2,0,334),(36085,195710,2,1,334),(36088,195710,2,0,334),(36084,195710,2,0,334),(36083,195710,2,0,334),(36091,195710,2,0,334),(36090,195710,2,0,334),(34458,195631,1,0,637),(34465,195631,1,0,637),(34463,195631,1,0,637),(34460,195631,1,0,637),(34459,195631,1,0,637),(34456,195631,1,0,637),(34466,195631,1,0,637),(34467,195631,1,0,637),(34468,195631,1,0,637),(34469,195631,1,0,637),(34470,195631,1,0,637),(34472,195631,1,0,637),(34474,195631,1,0,637),(34473,195631,1,0,637),(34455,195631,1,0,637),(34454,195631,1,0,637),(34453,195631,1,0,637),(34441,195631,1,1,637),(34471,195631,1,0,637),(34475,195631,1,0,637),(34444,195631,1,0,637),(34445,195631,1,0,637),(34447,195631,1,0,637),(34461,195631,1,0,637),(34448,195631,1,0,637),(34449,195631,1,0,637),(34450,195631,1,0,637),(34451,195631,1,0,637),(35686,195632,2,0,637),(35671,195632,2,0,637),(35683,195632,2,0,637),(35680,195632,2,0,637),(35674,195632,2,0,637),(35689,195632,2,0,637),(35721,195632,2,0,637),(35718,195632,2,0,637),(35731,195632,2,0,637),(35714,195632,2,0,637),(35711,195632,2,0,637),(35734,195632,2,0,637),(35737,195632,2,0,637),(35740,195632,2,0,637),(35743,195632,2,0,637),(35746,195632,2,0,637),(35708,195632,2,0,637),(35705,195632,2,0,637),(35702,195632,2,0,637),(35699,195632,2,0,637),(35695,195632,2,0,637),(35692,195632,2,0,637),(35728,195632,2,0,637),(35724,195632,2,0,637),(35668,195632,2,0,637),(34442,195632,2,1,637),(35662,195632,2,0,637),(35665,195632,2,0,637),(35725,195633,3,0,637),(35722,195633,3,0,637),(35719,195633,3,0,637),(35715,195633,3,0,637),(35709,195633,3,0,637),(35706,195633,3,0,637),(35729,195633,3,0,637),(35744,195633,3,0,637),(35732,195633,3,0,637),(35735,195633,3,0,637),(35738,195633,3,0,637),(35741,195633,3,0,637),(35747,195633,3,0,637),(35712,195633,3,0,637),(35703,195633,3,0,637),(35700,195633,3,0,637),(35672,195633,3,0,637),(35690,195633,3,0,637),(35687,195633,3,0,637),(35669,195633,3,0,637),(35684,195633,3,0,637),(35693,195633,3,0,637),(34443,195633,3,1,637),(35681,195633,3,0,637),(35663,195633,3,0,637),(35666,195633,3,0,637),(35696,195633,3,0,637),(35675,195633,3,0,637),(35700,195635,4,0,637),(35749,195635,4,1,637),(35706,195635,4,0,637),(35703,195635,4,0,637),(35709,195635,4,0,637),(35744,195635,4,0,637),(35741,195635,4,0,637),(35735,195635,4,0,637),(35732,195635,4,0,637),(35729,195635,4,0,637),(35725,195635,4,0,637),(35722,195635,4,0,637),(35719,195635,4,0,637),(35715,195635,4,0,637),(35712,195635,4,0,637),(35747,195635,4,0,637),(35696,195635,4,0,637),(35675,195635,4,0,637),(35681,195635,4,0,637),(35663,195635,4,0,637),(35669,195635,4,0,637),(35666,195635,4,0,637),(35738,195635,4,0,637),(35672,195635,4,0,637),(35684,195635,4,0,637),(35687,195635,4,0,637),(35690,195635,4,0,637),(35693,195635,4,0,637),(16064,181366,1,0,692),(16065,181366,1,0,692),(16063,181366,1,0,692),(30549,181366,1,1,692),(30602,193426,2,0,692),(30603,193426,2,0,692),(30601,193426,2,0,692),(30600,193426,2,1,692),(36948,202178,1,0,847),(36939,202178,1,0,847),(38157,202180,2,0,847),(38156,202180,2,0,847),(38639,202177,3,0,847),(38637,202177,3,0,847),(38640,202179,4,0,847),(38638,202179,4,0,847),(25740,187892,0,0,0),(12018,179703,0,0,0);
+/*!40000 ALTER TABLE `aowow_loot_link` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Dumping data for table `aowow_objectdifficulty`
+--
+
+LOCK TABLES `aowow_objectdifficulty` WRITE;
+/*!40000 ALTER TABLE `aowow_objectdifficulty` DISABLE KEYS */;
+INSERT INTO `aowow_objectdifficulty` VALUES (181366,193426,0,0,2),(193905,193967,0,0,2),(194307,194308,194200,194201,2),(194312,194314,194313,194315,2),(194324,194328,194327,194331,2),(194789,194956,194957,194958,2),(194821,194822,0,0,2),(195046,195047,0,0,2),(195631,195632,195633,195635,2),(202178,202180,202177,202179,2),(202239,202240,202238,202241,2),(201959,202339,202338,202340,2),(0,0,195668,195672,2),(0,0,195667,195671,2),(0,0,195666,195670,2),(0,0,195665,195669,2),(185168,185169,0,0,1),(184465,184849,0,0,1),(190586,193996,0,0,1),(190663,193597,0,0,1),(191349,193603,0,0,1),(195709,195710,0,0,1),(195323,195324,0,0,1),(195374,195375,0,0,1),(201710,202336,0,0,1);
+/*!40000 ALTER TABLE `aowow_objectdifficulty` ENABLE KEYS */;
+UNLOCK TABLES;
+/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */;
+
+--
+-- Dumping data for table `aowow_profiler_excludes`
+--
+
+LOCK TABLES `aowow_profiler_excludes` WRITE;
+/*!40000 ALTER TABLE `aowow_profiler_excludes` DISABLE KEYS */;
+INSERT INTO `aowow_profiler_excludes` VALUES (6,459,1,'Gray Wolf'),(6,468,1,'White Stallion'),(6,471,1,'Palamino'),(6,472,1,'Pinto'),(6,578,1,'Black Wolf'),(6,579,1,'Red Wolf'),(6,581,1,'Winter Wolf'),(6,3363,1,'Nether Drake'),(6,6896,1,'Black Ram'),(6,6897,1,'Blue Ram'),(6,8980,1,'Skeletal Horse'),(6,10681,1,'Summon Cockatoo'),(6,10686,1,'Summon Prairie Chicken'),(6,10687,1,'Summon White Plymouth Rock'),(6,10699,1,'Summon Bronze Whelpling'),(6,10700,1,'Summon Faeling'),(6,10701,1,'Summon Dart Frog'),(6,10702,1,'Summon Island Frog'),(6,10705,1,'Summon Eagle Owl'),(6,10708,1,'Summon Snowy Owl'),(6,10710,1,'Summon Cottontail Rabbit'),(6,10712,1,'Summon Spotted Rabbit'),(6,10715,1,'Summon Blue Racer'),(6,10718,1,'Green Water Snake'),(6,10719,1,'Ribbon Snake'),(6,10720,1,'Scarlet Snake'),(6,10721,1,'Summon Elven Wisp'),(6,10795,1,'Ivory Raptor'),(6,10798,1,'Obsidian Raptor'),(6,15648,1,'Corrupted Kitten'),(6,15779,1,'White Mechanostrider Mod B'),(6,15780,1,'Green Mechanostrider'),(6,15781,1,'Steel Mechanostrider'),(6,16055,1,'Black Nightsaber'),(6,16056,1,'Ancient Frostsaber'),(6,16058,1,'Primal Leopard'),(6,16059,1,'Tawny Sabercat'),(6,16060,1,'Golden Sabercat'),(6,16080,1,'Red Wolf'),(6,16081,1,'Winter Wolf'),(6,16082,1,'Palomino'),(6,16083,1,'White Stallion'),(6,16084,1,'Mottled Red Raptor'),(6,17450,1,'Ivory Raptor'),(6,17455,1,'Purple Mechanostrider'),(6,17456,1,'Red and Blue Mechanostrider'),(6,17458,1,'Fluorescent Green Mechanostrider'),(6,17459,1,'Icy Blue Mechanostrider Mod A'),(6,17460,1,'Frost Ram'),(6,17461,1,'Black Ram'),(6,17468,1,'Pet Fish'),(6,17469,1,'Pet Stone'),(6,18363,1,'Riding Kodo'),(6,18991,1,'Green Kodo'),(6,18992,1,'Teal Kodo'),(6,19363,1,'Summon Mechanical Yeti'),(6,23220,1,'Swift Dawnsaber'),(6,23428,1,'Albino Snapjaw'),(6,23429,1,'Loggerhead Snapjaw'),(6,23430,1,'Olive Snapjaw'),(6,23431,1,'Leatherback Snapjaw'),(6,23432,1,'Hawksbill Snapjaw'),(6,23530,16,'Tiny Red Dragon - wrong region'),(6,23531,16,'Tiny Green Dragon - wrong region'),(6,24985,1,'Summon Baby Murloc (Blue)'),(6,24986,1,'Summon Baby Murloc (Green)'),(6,24987,1,'Summon Baby Murloc (Orange)'),(6,24988,4,'Lurky - CE'),(6,24989,1,'Summon Baby Murloc (Pink)'),(6,24990,1,'Summon Baby Murloc (Purple)'),(6,25849,1,'Baby Shark'),(6,26067,1,'Summon Mechanical Greench'),(6,26391,1,'Tentacle Call'),(6,28828,1,'Nether Drake'),(6,29059,1,'Naxxramas Deathcharger'),(6,30152,1,'White Tiger Cub'),(6,30156,2,'Hippogryph Hatchling - TCG loot'),(6,30174,2,'Riding Turtle - TCG loot'),(6,32298,4,'Netherwhelp - CE'),(6,32345,1,'Peep the Phoenix Mount'),(6,33050,128,'Magical Crawdad'),(6,33057,1,'Summon Mighty Mr. Pinchy'),(6,33630,1,'Blue Mechanostrider'),(6,34407,1,'Great Elite Elekk'),(6,35157,1,'Summon Spotted Rabbit'),(6,37015,1,'Swift Nether Drake'),(6,40319,16,'Lucky - wrong region'),(6,40405,16,'Lucky - wrong region'),(6,43688,1,'Amani War Bear'),(6,43810,1,'Frost Wyrm'),(6,44317,1,'Merciless Nether Drake'),(6,44744,1,'Merciless Nether Drake'),(6,45125,2,'Rocket Chicken - TCG loot'),(6,45174,16,'Golden Pig - wrong region'),(6,45175,16,'Silver Pig - wrong region'),(6,45890,1,'Scorchling'),(6,47037,1,'Swift War Elekk'),(6,48406,16,'Essence of Competition - wrong region'),(6,48408,16,'Essence of Competition - wrong region'),(6,48954,8,'Swift Zhevra - promotion'),(6,49322,8,'Swift Zhevra - promotion'),(6,49378,1,'Brewfest Riding Kodo'),(6,50869,1,'Brewfest Kodo'),(6,50870,1,'Brewfest Ram'),(6,51851,1,'Vampiric Batling'),(6,51960,1,'Frost Wyrm Mount'),(6,52615,4,'Frosty - CE'),(6,53082,8,'Mini Tyrael - promotion'),(6,53768,1,'Haunted'),(6,54187,1,'Clockwork Rocket Bot'),(6,55068,1,'Mr. Chilly'),(6,58983,8,'Big Blizzard Bear - promotion'),(6,59572,1,'Black Polar Bear'),(6,59573,1,'Brown Polar Bear'),(6,59802,1,'Grand Ice Mammoth'),(6,59804,1,'Grand Ice Mammoth'),(6,59976,1,'Black Proto-Drake'),(6,60021,1,'Plagued Proto-Drake'),(6,60136,1,'Grand Caravan Mammoth'),(6,60140,1,'Grand Caravan Mammoth'),(6,61442,1,'Swift Mooncloth Carpet'),(6,61444,1,'Swift Shadoweave Carpet'),(6,61446,1,'Swift Spellfire Carpete'),(6,61855,1,'Baby Blizzard Bear'),(6,62048,1,'Black Dragonhawk Mount'),(6,62514,1,'Alarming Clockbot'),(6,63318,8,'Murkimus the Gladiator'),(6,64351,1,'XS-001 Constructor Bot'),(6,64656,1,'Blue Skeletal Warhorse'),(6,64731,128,'Sea Turtle - fishing only'),(6,65682,1,'Warbot'),(6,65917,2,'Magic Rooster - TCG loot'),(6,66030,8,'Grunty - promotion'),(6,66520,1,'Jade Tiger'),(6,66907,1,'Argent Warhorse'),(6,67527,16,'Onyx Panther - wrong region'),(6,68767,2,'Tuskarr Kite - TCG loot'),(6,68810,2,'Spectral Tiger Cub - TCG loot'),(6,69002,1,'Onyxian Whelpling'),(6,69452,8,'Core Hound Pup - promotion'),(6,69535,4,'Gryphon Hatchling - CE'),(6,69536,4,'Wind Rider Cub - CE'),(6,69539,1,'Zipao Tiger'),(6,69541,4,'Pandaren Monk - CE'),(6,69677,4,'Lil\' K.T. - CE'),(6,74856,2,'Blazing Hippogryph - TCG loot'),(6,74918,2,'Wooly White Rhino - TCG loot'),(6,75613,1,'Celestial Dragon'),(6,75614,1,'Celestial Steed - unavailable'),(6,75906,4,'Lil\' XT - CE'),(6,75936,1,'Murkimus the Gladiator'),(6,75973,8,'X-53 Touring Rocket - promotion'),(6,78381,8,'Mini Thor - promotion'),(8,87,1024,'Bloodsail Buccaneers - max rank is honored'),(8,92,1024,'Gelkis Clan Centaur - max rank is friendly'),(8,93,1024,'Magram Clan Centaur - max rank is friendly'),(6,46197,2,'X-51 Nether-Rocket - TCG loot'),(6,46199,2,'X-51 Nether-Rocket X-TREME - TCG loot'),(6,26656,1,'Black Qiraji Battle Tank - unavailable'),(6,43899,1,'Brewfest Ram - unavailable'),(6,49193,1,'Vengeful Nether Drake - unavailable'),(6,58615,1,'Brutal Nether Drake - unavailable'),(6,64927,1,'Deadly Gladiator\'s Frost Wyrm - unavailable'),(6,65439,1,'Furious Gladiator\'s Frost Wyrm - unavailable'),(6,67336,1,'Relentless Gladiator\'s Frost Wyrm - unavailable'),(6,71810,1,'Wrathful Gladiator\'s Frost Wyrm - unavailable'),(11,122,1,'RealmFirst Kel\'T Title - unavailable'),(11,159,1,'RealmFirst Algalon Title - unavailable'),(11,120,1,'RealmFirst Maly Title - unavailable'),(11,170,1,'RealmFirst TotGC Title - unavailable'),(11,139,1,'RealmFirst Sarth Title - unavailable'),(11,158,1,'RealmFirst Yogg Title - unavailable'),(6,28505,8,'Poley - promotion'),(6,28487,1,'Terky - unavailable'),(8,70,1024,'Syndicate - max rank is neutral'),(6,28242,1,'Icebane Breastplate'),(6,28243,1,'Icebane Gauntlets'),(6,28244,1,'Icebane Bracers'),(6,16986,1,'Blood Talon'),(6,16987,1,'Darkspear'),(6,16965,1,'Bleakwood Hew'),(6,8366,1,'Ironforge Chain'),(6,8368,1,'Ironforge Gauntlets'),(6,9942,1,'Mithril Scale Gloves'),(6,2671,1,'Rough Bronze Bracers'),(6,16980,1,'Rune Edge'),(6,16960,1,'Thorium Greatsword'),(6,16967,1,'Inlaid Thorium Hammer'),(6,30342,1,'Red Smoke Flare'),(6,30343,1,'Blue Smoke Flare'),(6,28205,1,'Glacial Gloves'),(6,28207,1,'Glacial Vest'),(6,28208,1,'Glacial Cloak'),(6,28209,1,'Glacial Wrists'),(6,28222,1,'Icy Scale Breastplate'),(6,28223,1,'Icy Scale Gauntlets'),(6,28224,1,'Icy Scale Bracers'),(6,28219,1,'Polar Tunic'),(6,28220,1,'Polar Gloves'),(6,28221,1,'Polar Bracers'),(6,28021,1,'Arcane Dust'),(6,44612,1,'Enchant Gloves - Greater Blasting'),(6,62257,1,'Enchant Weapon - Titanguard'),(6,31461,1,'Heavy Netherweave Net'),(6,56048,1,'Duskweave Boots'),(6,7636,1,'Green Woolen Robe'),(6,8778,1,'Boots of Darkness'),(6,12062,1,'Stormcloth Pants'),(6,12063,1,'Stormcloth Gloves'),(6,12068,1,'Stormcloth Vest'),(6,12083,1,'Stormcloth Headband'),(6,12087,1,'Stormcloth Shoulders'),(6,12090,1,'Stormcloth Boots');
+/*!40000 ALTER TABLE `aowow_profiler_excludes` ENABLE KEYS */;
+UNLOCK TABLES;
+
+--
+-- Dumping data for table `aowow_quickfacts`
+--
+
+LOCK TABLES `aowow_quickfacts` WRITE;
+/*!40000 ALTER TABLE `aowow_quickfacts` DISABLE KEYS */;
+INSERT INTO `aowow_quickfacts` VALUES (7,1,1,'|L:zone:city||L:main:colon|[zone=1537]'),(7,12,1,'|L:zone:city||L:main:colon|[zone=1519]'),(7,14,1,'|L:zone:city||L:main:colon|[zone=1637]'),(7,65,1,'|L:zone:reputationHub|[faction=1091]'),(7,67,1,'|L:zone:reputationHub|[faction=1119]'),(7,85,1,'|L:zone:city||L:main:colon|[zone=1497]'),(7,139,1,'|L:zone:reputationHub|[faction=529]'),(7,141,1,'|L:zone:city||L:main:colon|[zone=1657]'),(7,206,1,'|L:zone:boss|[icon preset=boss][npc=23954][/icon]'),(7,209,1,'|L:zone:boss|[icon preset=boss][npc=4275][/icon]'),(7,210,1,'|L:zone:reputationHub|[faction=1106]\n[faction=1098]'),(7,215,1,'|L:zone:city||L:main:colon|[zone=1638]'),(7,361,1,'|L:zone:reputationHub|[faction=576]'),(7,405,1,'|L:zone:reputationHub|[faction=92]\n[faction=93]'),(7,440,1,'|L:zone:reputationHub|[faction=989]'),(7,491,1,'|L:zone:boss|[icon preset=boss][npc=4421][/icon]'),(7,493,1,'|L:game:class||L:main:colon|[class=11]'),(7,618,1,'|L:zone:reputationHub|[faction=589]'),(7,717,1,'|L:zone:boss|[icon preset=boss][npc=1716][/icon]'),(7,718,1,'|L:zone:boss|[icon preset=boss][npc=5775][/icon]'),(7,719,1,'|L:zone:boss|[icon preset=boss][npc=4829][/icon]'),(7,721,1,'|L:zone:boss|[icon preset=boss][npc=7800][/icon]'),(7,722,1,'|L:zone:boss|[icon preset=boss][npc=7358][/icon]'),(7,796,1,'|L:zone:key:0|[item=7146]'),(7,796,2,'|L:zone:boss|[icon preset=boss][npc=3976][/icon]'),(7,1176,1,'|L:zone:boss|[icon preset=boss][npc=7267][/icon]'),(7,1196,1,'|L:zone:boss|[icon preset=boss][npc=26861][/icon]'),(7,1337,1,'|L:zone:boss|[icon preset=boss][npc=2748][/icon]'),(7,1377,1,'|L:zone:reputationHub|[faction=609]'),(7,1477,1,'|L:zone:boss|[icon preset=boss][npc=5709][/icon]'),(7,1497,1,'|L:zone:location|[zone=85]'),(7,1497,2,'|L:zone:reputationHub|[faction=68]'),(7,1519,1,'|L:zone:location|[zone=12]'),(7,1519,2,'|L:zone:reputationHub|[faction=72]'),(7,1537,1,'|L:zone:location|[zone=1]'),(7,1537,2,'|L:zone:reputationHub|[faction=47]\n[faction=54]'),(7,1581,1,'|L:zone:boss|[icon preset=boss][npc=639][/icon]'),(7,1583,1,'|L:zone:boss|[icon preset=boss][npc=10363][/icon]'),(7,1584,1,'|L:zone:boss|[icon preset=boss][npc=9019][/icon]'),(7,1637,1,'|L:zone:location|[zone=14]'),(7,1637,2,'|L:zone:reputationHub|[faction=76]'),(7,1638,1,'|L:zone:location|[zone=215]'),(7,1638,2,'|L:zone:reputationHub|[faction=81]'),(7,1657,1,'|L:zone:location|[zone=141]'),(7,1657,2,'|L:zone:reputationHub|[faction=69]'),(7,1977,1,'|L:zone:raidFaction|[faction=270]'),(7,1977,2,'|L:zone:boss|[icon preset=boss][npc=14834][/icon]'),(7,2017,1,'|L:zone:boss|[icon preset=boss][npc=10440][/icon]'),(7,2057,1,'|L:zone:key:0|[item=13704]'),(7,2057,2,'|L:zone:boss|[icon preset=boss][npc=1853][/icon]'),(7,2100,1,'|L:zone:boss|[icon preset=boss][npc=12201][/icon]'),(7,2366,1,'|L:zone:faction|[faction=989]'),(7,2366,2,'|L:zone:boss|[icon preset=boss][npc=17881][/icon]'),(7,2367,1,'|L:zone:faction|[faction=989]'),(7,2367,2,'|L:zone:boss|[icon preset=boss][npc=18096][/icon]'),(7,2437,1,'|L:zone:boss|[icon preset=boss][npc=11520][/icon]'),(7,2677,1,'|L:zone:attunement:0|[quest=7761]'),(7,2677,2,'|L:zone:boss|[icon preset=boss][npc=11583][/icon]'),(7,2717,1,'|L:zone:attunement:0|[quest=7487]'),(7,2717,2,'|L:zone:raidFaction|[faction=749]'),(7,2717,3,'|L:zone:boss|[icon preset=boss][npc=11502][/icon]'),(7,3428,1,'|L:zone:raidFaction|[faction=910]'),(7,3428,2,'|L:zone:boss|[icon preset=boss][npc=15727][/icon]'),(7,3429,1,'|L:zone:raidFaction|[faction=609]'),(7,3429,2,'|L:zone:boss|[icon preset=boss][npc=15339][/icon]'),(7,3430,1,'|L:zone:city||L:main:colon|[zone=3487]'),(7,3433,1,'|L:zone:reputationHub|[faction=922]'),(7,3457,1,'|L:zone:attunement:0|[quest=9837]'),(7,3457,2,'|L:zone:key:0|[item=24490]'),(7,3457,3,'|L:zone:raidFaction|[faction=967]'),(7,3457,4,'|L:zone:boss|[icon preset=boss][npc=15690][/icon]'),(7,3483,1,'|L:zone:reputationHub|[icon name=side_alliance][faction=946][/icon]\n[icon name=side_horde][faction=947][/icon]'),(7,3487,1,'|L:zone:location|[zone=3430]'),(7,3487,2,'|L:zone:reputationHub|[faction=911]'),(7,3518,1,'|L:zone:reputationHub|[icon name=side_alliance][faction=978][/icon]\n[icon name=side_horde][faction=941][/icon]'),(7,3519,1,'|L:zone:reputationHub|[faction=1031]'),(7,3519,2,'|L:zone:city||L:main:colon|[zone=3703]'),(7,3520,1,'|L:zone:reputationHub|[faction=1015]'),(7,3521,1,'|L:zone:reputationHub|[faction=942]\n[faction=970]'),(7,3522,1,'|L:zone:reputationHub|[faction=1038]'),(7,3523,1,'|L:zone:reputationHub|[faction=933]'),(7,3557,1,'|L:zone:location|[zone=3524]'),(7,3557,2,'|L:zone:reputationHub|[faction=930]'),(7,3562,1,'|L:zone:faction|[icon name=side_alliance][faction=946][/icon] / [icon name=side_horde][faction=947][/icon]'),(7,3562,2,'|L:zone:boss|[icon preset=boss][npc=17536][/icon]'),(7,3606,1,'|L:zone:raidFaction|[faction=990]'),(7,3606,2,'|L:zone:boss|[icon preset=boss][npc=17968][/icon]'),(7,3607,1,'|L:zone:boss|[icon preset=boss][npc=21212][/icon]'),(7,3703,1,'|L:zone:location|[zone=3519]'),(7,3703,2,'|L:zone:reputationHub|[faction=932]\n[faction=934]\n[faction=1011]'),(7,3711,1,'|L:zone:reputationHub|[faction=1105]\n[faction=1104]'),(7,3713,1,'|L:zone:faction|[icon name=side_alliance][faction=946][/icon] / [icon name=side_horde][faction=947][/icon]'),(7,3713,2,'|L:zone:boss|[icon preset=boss][npc=17377][/icon]'),(7,3714,1,'|L:zone:key:0|[item=28395]'),(7,3714,2,'|L:zone:faction|[icon name=side_alliance][faction=946][/icon] / [icon name=side_horde][faction=947][/icon]'),(7,3714,3,'|L:zone:boss|[icon preset=boss][npc=16808][/icon]'),(7,3715,1,'|L:zone:faction|[faction=942]'),(7,3715,2,'|L:zone:boss|[icon preset=boss][npc=17798][/icon]'),(7,3716,1,'|L:zone:faction|[faction=942]'),(7,3716,2,'|L:zone:boss|[icon preset=boss][npc=17882][/icon]'),(7,3717,1,'|L:zone:faction|[faction=942]'),(7,3717,2,'|L:zone:boss|[icon preset=boss][npc=17942][/icon]'),(7,3789,1,'|L:zone:key:0|[item=27991]'),(7,3789,2,'|L:zone:faction|[faction=1011]'),(7,3789,3,'|L:zone:boss|[icon preset=boss][npc=18708][/icon]'),(7,3790,1,'|L:zone:faction|[faction=1011]'),(7,3790,2,'|L:zone:boss|[icon preset=boss][npc=18373][/icon]'),(7,3791,1,'|L:zone:faction|[faction=1011]'),(7,3791,2,'|L:zone:boss|[icon preset=boss][npc=18473][/icon]'),(7,3792,1,'|L:zone:faction|[faction=933]'),(7,3792,2,'|L:zone:boss|[icon preset=boss][npc=18344][/icon]'),(7,3805,1,'|L:zone:boss|[icon preset=boss][npc=23863][/icon]'),(7,3836,1,'|L:zone:boss|[icon preset=boss][npc=17257][/icon]'),(7,3845,1,'|L:zone:boss|[icon preset=boss][npc=19622][/icon]'),(7,3847,1,'|L:zone:faction|[faction=935]'),(7,3847,2,'|L:zone:boss|[icon preset=boss][npc=17977][/icon]'),(7,3848,1,'|L:zone:key:0|[item=31084]'),(7,3848,2,'|L:zone:faction|[faction=935]'),(7,3848,3,'|L:zone:boss|[icon preset=boss][npc=20912][/icon]'),(7,3849,1,'|L:zone:faction|[faction=935]'),(7,3849,2,'|L:zone:boss|[icon preset=boss][npc=19220][/icon]'),(7,3923,1,'|L:zone:boss|[icon preset=boss][npc=19044][/icon]'),(7,3959,1,'|L:zone:raidFaction|[faction=1012]'),(7,3959,2,'|L:zone:boss|[icon preset=boss][npc=22917][/icon]'),(7,4075,1,'|L:zone:boss|[icon preset=boss][npc=25315][/icon]'),(7,4080,1,'|L:zone:reputationHub|[faction=1077]'),(7,4100,1,'|L:zone:boss|[icon preset=boss][npc=26533][/icon]'),(7,4131,1,'|L:zone:faction|[faction=1077]'),(7,4131,2,'|L:zone:boss|[icon preset=boss][npc=24664][/icon]'),(7,4196,1,'|L:zone:boss|[icon preset=boss][npc=26632][/icon]'),(7,4228,1,'|L:zone:boss|[icon preset=boss][npc=27656][/icon]'),(7,4264,1,'|L:zone:boss|[icon preset=boss][npc=27978][/icon]'),(7,4265,1,'|L:zone:boss|[icon preset=boss][npc=26723][/icon]'),(7,4272,1,'|L:zone:boss|[icon preset=boss][npc=28923][/icon]'),(7,4277,1,'|L:zone:boss|[icon preset=boss][npc=29120][/icon]'),(7,4395,1,'|L:zone:location|[zone=2817]'),(7,4395,2,'|L:zone:reputationHub|[faction=1090]'),(7,4415,1,'|L:zone:boss|[icon preset=boss][npc=31134][/icon]'),(7,4416,1,'|L:zone:boss|[icon preset=boss][npc=29306][/icon]'),(7,4494,1,'|L:zone:boss|[icon preset=boss][npc=29311][/icon]'),(7,4723,1,'|L:zone:boss|[icon preset=boss][npc=35451][/icon]'),(7,4809,1,'|L:zone:boss|[icon preset=boss][npc=36502][/icon]'),(7,4813,1,'|L:zone:boss|[icon preset=boss][npc=36658][/icon]'),(7,4820,1,'|L:zone:boss|[icon preset=boss][npc=36954][/icon]');
+/*!40000 ALTER TABLE `aowow_quickfacts` ENABLE KEYS */;
+UNLOCK TABLES;
+/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */;
+
+--
+-- Dumping data for table `aowow_setup_custom_data`
+--
+
+LOCK TABLES `aowow_setup_custom_data` WRITE;
+/*!40000 ALTER TABLE `aowow_setup_custom_data` DISABLE KEYS */;
+INSERT INTO `aowow_setup_custom_data` VALUES ('zones',2257,'cuFlags','0','Deeprun Tram - make visible'),('zones',2257,'category','0','Deeprun Tram - Category: Eastern Kingdoms'),('zones',2257,'type','1','Deeprun Tram - Type: Transit'),('zones',3698,'expansion','1','Nagrand Arena - Addon: BC'),('zones',3702,'expansion','1','Blades Edge Arena - Addon: BC'),('zones',3968,'expansion','1','Ruins of Lordaeron Arena - Addon: BC'),('zones',4378,'expansion','1','Dalaran Arena - Addon: WotLK'),('zones',4406,'expansion','1','Ring of Valor Arena - Addon: WotLK'),('zones',2597,'maxPlayer','40','Alterac Valey - Players: 40 [battlemasterlist.dbc: 5]'),('zones',4710,'maxPlayer','40','Isle of Conquest - Players: 40 [battlemasterlist.dbc: 5]'),('zones',4893,'cuFlags','1073741824','The Frost Queen\'s Lair - set: CUSTOM_EXCLUDE_FOR_LISTVIEW'),('zones',4894,'cuFlags','1073741824','Putricide\'s Laboratory [..] - set: CUSTOM_EXCLUDE_FOR_LISTVIEW'),('achievement',1956,'itemExtra','44738','Higher Learning - item rewarded through gossip'),('zones',4895,'cuFlags','1073741824','The Crimson Hall - set: CUSTOM_EXCLUDE_FOR_LISTVIEW'),('titles',137,'gender','2','Matron - female'),('zones',4896,'cuFlags','1073741824','The Frozen Throne - set: CUSTOM_EXCLUDE_FOR_LISTVIEW'),('zones',4897,'cuFlags','1073741824','The Sanctum of Blood - set: CUSTOM_EXCLUDE_FOR_LISTVIEW'),('zones',4076,'cuFlags','1073741824','Reuse Me 7 - set: CUSTOM_EXCLUDE_FOR_LISTVIEW'),('zones',207,'cuFlags','1073741824','The Great Sea - set: CUSTOM_EXCLUDE_FOR_LISTVIEW'),('zones',208,'cuFlags','1073741824','Unused Ironcladcove - set: CUSTOM_EXCLUDE_FOR_LISTVIEW'),('zones',2817,'levelMin','74','Crystalsong Forest - missing lfgDungeons entry'),('zones',1417,'cuFlags','1073741824','Sunken Temple [extra area on map 109] - set: CUSTOM_EXCLUDE_FOR_LISTVIEW'),('zones',22,'cuFlags','1073741824','Programmer Isle - set: CUSTOM_EXCLUDE_FOR_LISTVIEW'),('zones',151,'cuFlags','1073741824','Designer Island - set: CUSTOM_EXCLUDE_FOR_LISTVIEW'),('zones',3948,'cuFlags','1073741824','Brian and Pat Test - set: CUSTOM_EXCLUDE_FOR_LISTVIEW'),('zones',4019,'cuFlags','1073741824','Development Land - set: CUSTOM_EXCLUDE_FOR_LISTVIEW'),('zones',3605,'cuFlags','1073741824','Hyjal Past [extra area on map 560] - set: CUSTOM_EXCLUDE_FOR_LISTVIEW'),('zones',3535,'cuFlags','1073741824','Hellfire Citadel [extra area on map 540] - set: CUSTOM_EXCLUDE_FOR_LISTVIEW'),('zones',41,'levelMin','50','Deadwind Pass - missing lfgDungeons entry'),('zones',41,'levelMax','60','Deadwind Pass - missing lfgDungeons entry'),('zones',2257,'levelMin','1','Deeprun Tram - missing lfgDungeons entry'),('zones',2257,'levelMax','80','Deeprun Tram - missing lfgDungeons entry'),('zones',4298,'category','0','Plaguelands: The Scarlet Enclave - Parent: Eastern Kingdoms'),('zones',4298,'levelMin','55','Plaguelands: The Scarlet Enclave - missing lfgDungeons entry'),('zones',4298,'levelMax','58','Plaguelands: The Scarlet Enclave - missing lfgDungeons entry'),('zones',493,'levelMin','15','Moonglade - missing lfgDungeons entry'),('zones',493,'levelMax','60','Moonglade - missing lfgDungeons entry'),('zones',2817,'levelMax','76','Crystalsong Forest - missing lfgDungeons entry'),('zones',4742,'levelMin','77','Hrothgar\'s Landing - missing lfgDungeons entry'),('zones',4742,'levelMax','80','Hrothgar\'s Landing - missing lfgDungeons entry'),('classes',1,'roles','10','Warrior - rngDPS'),('classes',2,'roles','11','Paladin - mleDPS + Tank + Heal'),('classes',3,'roles','4','Hunter - rngDPS'),('classes',4,'roles','2','Rogue - mleDPS'),('classes',5,'roles','5','Priest - rngDPS + Heal'),('classes',6,'roles','10','Death Knight - mleDPS + Tank'),('classes',7,'roles','7','Shaman - mleDPS + rngDPS + Heal'),('classes',8,'roles','4','Mage - rngDPS'),('classes',9,'roles','4','Warlock - rngDPS'),('classes',11,'roles','15','Druid - mleDPS + Tank + Heal + rngDPS'),('currencies',103,'cap','10000','Arena Points - cap'),('currencies',104,'cap','75000','Honor Points - cap'),('currencies',1,'cuFlags','1073741824','Currency Token Test Token 1 - set: CUSTOM_EXCLUDE_FOR_LISTVIEW'),('currencies',2,'cuFlags','1073741824','Currency Token Test Token 2 - set: CUSTOM_EXCLUDE_FOR_LISTVIEW'),('currencies',4,'cuFlags','1073741824','Currency Token Test Token 3 - set: CUSTOM_EXCLUDE_FOR_LISTVIEW'),('currencies',22,'cuFlags','1073741824','Birmingham Test Item 3 - set: CUSTOM_EXCLUDE_FOR_LISTVIEW'),('currencies',141,'cuFlags','1073741824','zzzOLDDaily Quest Faction Token - set: CUSTOM_EXCLUDE_FOR_LISTVIEW'),('currencies',1,'category','3','Currency Token Test Token 1 - category: unused'),('currencies',2,'category','3','Currency Token Test Token 2 - category: unused'),('currencies',4,'category','3','Currency Token Test Token 3 - category: unused'),('currencies',22,'category','3','Birmingham Test Item 3 - category: unused'),('currencies',141,'category','3','zzzOLDDaily Quest Faction Token - category: unused'),('factions',68,'qmNpcIds','33555','Undercity - set Quartermaster'),('factions',47,'qmNpcIds','33310','Ironforge - set Quartermaster'),('factions',69,'qmNpcIds','33653','Darnassus - set Quartermaster'),('factions',72,'qmNpcIds','33307','Stormwind - set Quartermaster'),('factions',76,'qmNpcIds','33553','Orgrimmar - set Quartermaster'),('factions',81,'qmNpcIds','33556','Thunder Bluff - set Quartermaster'),('factions',922,'qmNpcIds','16528','Tranquillien - set Quartermaster'),('factions',930,'qmNpcIds','33657','Exodar - set Quartermaster'),('factions',932,'qmNpcIds','19321','The Aldor - set Quartermaster'),('factions',933,'qmNpcIds','20242 23007','The Consortium - set Quartermaster'),('factions',935,'qmNpcIds','21432','The Sha\'tar - set Quartermaster'),('factions',941,'qmNpcIds','20241','The Mag\'har - set Quartermaster'),('factions',942,'qmNpcIds','17904','Cenarion Expedition - set Quartermaster'),('factions',946,'qmNpcIds','17657','Honor Hold - set Quartermaster'),('factions',947,'qmNpcIds','17585','Thrallmar - set Quartermaster'),('factions',970,'qmNpcIds','18382','Sporeggar - set Quartermaster'),('factions',978,'qmNpcIds','20240','Kurenai - set Quartermaster'),('factions',989,'qmNpcIds','21643','Keepers of Time - set Quartermaster'),('factions',1011,'qmNpcIds','21655','Lower City - set Quartermaster'),('factions',1012,'qmNpcIds','23159','Ashtongue Deathsworn - set Quartermaster'),('factions',1037,'qmNpcIds','32773 32564','Alliance Vanguard - set Quartermaster'),('factions',1038,'qmNpcIds','23428','Ogri\'la - set Quartermaster'),('factions',1052,'qmNpcIds','32774 32565','Horde Expedition - set Quartermaster'),('factions',1073,'qmNpcIds','31916 32763','The Kalu\'ak - set Quartermaster'),('factions',1090,'qmNpcIds','32287','Kirin Tor - set Quartermaster'),('factions',1091,'qmNpcIds','32533','The Wyrmrest Accord - set Quartermaster'),('factions',1094,'qmNpcIds','34881','The Silver Covenant - set Quartermaster'),('factions',1105,'qmNpcIds','31910','The Oracles - set Quartermaster'),('factions',1106,'qmNpcIds','30431','Argent Crusade - set Quartermaster'),('factions',1119,'qmNpcIds','32540','The Sons of Hodir - set Quartermaster'),('factions',1124,'qmNpcIds','34772','The Sunreavers - set Quartermaster'),('factions',1156,'qmNpcIds','37687','The Ashen Verdict - set Quartermaster'),('factions',1082,'cuFlags','1073741824','REUSE - set: CUSTOM_EXCLUDE_FOR_LISTVIEW'),('factions',952,'cuFlags','1073741824','Test Faction 3 - set: CUSTOM_EXCLUDE_FOR_LISTVIEW'),('titles',138,'gender','1','Patron - male'),('sounds',15407,'cat','10','UR_Algalon_Summon03 - is not an item pickup'),('shapeshiftforms',1,'displayIdH','8571','Cat Form - spellshapeshiftform.dbc missing displayId'),('shapeshiftforms',15,'displayIdH','8571','Creature - Cat - spellshapeshiftform.dbc missing displayId'),('shapeshiftforms',5,'displayIdH','2289','Bear Form - spellshapeshiftform.dbc missing displayId'),('shapeshiftforms',8,'displayIdH','2289','Dire Bear Form - spellshapeshiftform.dbc missing displayId'),('shapeshiftforms',14,'displayIdH','2289','Creature - Bear - spellshapeshiftform.dbc missing displayId'),('shapeshiftforms',27,'displayIdH','21244','Flight Form, Epic - spellshapeshiftform.dbc missing displayId'),('shapeshiftforms',29,'displayIdH','20872','Flight Form - spellshapeshiftform.dbc missing displayId'),('races',1,'leader','29611','Human - King Varian Wrynn'),('races',1,'factionId','72','Human - Stormwind'),('races',1,'startAreaId','12','Human - Elwynn Forest'),('races',2,'leader','4949','Orc - Thrall'),('races',2,'factionId','76','Orc - Orgrimmar'),('races',2,'startAreaId','14','Orc - Durotar'),('races',3,'leader','2784','Dwarf - King Magni Bronzebeard'),('races',3,'factionId','47','Dwarf - Ironforge'),('races',3,'startAreaId','1','Dwarf - Dun Morogh'),('races',4,'leader','7999','Night Elf - Tyrande Whisperwind'),('races',4,'factionId','69','Night Elf - Darnassus'),('races',4,'startAreaId','141','Night Elf - Teldrassil'),('races',5,'leader','10181','Undead - Lady Sylvanas Windrunner'),('races',5,'factionId','68','Undead - Undercity'),('races',5,'startAreaId','85','Undead - Tirisfal Glades'),('races',6,'leader','3057','Tauren - Cairne Bloodhoof'),('races',6,'factionId','81','Tauren - Thunder Bluff'),('races',6,'startAreaId','215','Tauren - Mulgore'),('races',7,'leader','7937','Gnome - High Tinker Mekkatorque'),('races',7,'factionId','54','Gnome - Gnomeregan Exiles'),('races',7,'startAreaId','1','Gnome - Dun Morogh'),('races',8,'leader','10540','Troll - Vol\'jin'),('races',8,'factionId','530','Troll - Darkspear Trolls'),('races',8,'startAreaId','14','Troll - Durotar'),('races',10,'leader','16802','Blood Elf - Lor\'themar Theron'),('races',10,'factionId','911','Blood Elf - Silvermoon City'),('races',10,'startAreaId','3430','Blood Elf - Eversong Woods'),('races',11,'leader','17468','Draenei - Prophet Velen'),('races',11,'factionId','930','Draenei - Exodar'),('races',11,'startAreaId','3524','Draenei - Azuremyst Isle'),('holidays',141,'achievementCatOrId','156','Feast of Winter Veil - Category: Feast of Winter Veil'),('holidays',181,'achievementCatOrId','159','Noblegarden - Category: Noblegarden'),('holidays',201,'achievementCatOrId','163','Children\'s Week - Category: Children\'s Week'),('holidays',324,'achievementCatOrId','158','Hallow\'s End - Category: Hallow\'s End'),('holidays',327,'achievementCatOrId','160','Lunar Festival - Category: Lunar Festival'),('holidays',341,'achievementCatOrId','161','Midsummer Fire Festival - Category: Midsummer Fire Festival'),('holidays',372,'achievementCatOrId','162','Brewfest - Category: Brewfest'),('holidays',398,'achievementCatOrId','-3457','Pirates\' Day - Achievement: The Captain\'s Booty'),('holidays',404,'achievementCatOrId','14981','Pilgrim\'s Bounty - Category: Pilgrim\'s Bounty'),('holidays',409,'achievementCatOrId','-3456','Day of the Dead - Achievement: Dead Man\'s Party'),('holidays',423,'achievementCatOrId','187','Love is in the Air - Category: Love is in the Air'),('holidays',324,'bossCreature','23682','Hallow\'s End - Headless Horseman'),('holidays',327,'bossCreature','15467','Lunar Festival - Omen'),('holidays',341,'bossCreature','25740','Midsummer Fire Festival - Ahune'),('holidays',372,'bossCreature','23872','Brewfest - Coren Direbrew'),('holidays',423,'bossCreature','36296','Love is in the Air - Apothecary Hummel'),('skillline',197,'professionMask','512','Tailoring'),('skillline',186,'professionMask','256','Mining'),('skillline',165,'specializations','10656 10658 10660','Leatherworking'),('skillline',165,'recipeSubClass','1','Leatherworking'),('skillline',165,'professionMask','128','Leatherworking'),('skillline',755,'recipeSubClass','10','Jewelcrafting'),('skillline',755,'professionMask','64','Jewelcrafting'),('skillline',129,'recipeSubClass','7','First Aid'),('skillline',129,'professionMask','32','First Aid'),('skillline',202,'specializations','20219 20222','Engineering'),('skillline',202,'recipeSubClass','3','Engineering'),('skillline',202,'professionMask','16','Engineering'),('skillline',333,'recipeSubClass','8','Enchanting'),('skillline',333,'professionMask','8','Enchanting'),('skillline',185,'recipeSubClass','5','Cooking'),('skillline',185,'professionMask','4','Cooking'),('skillline',164,'specializations','9788 9787 17041 17040 17039','Blacksmithing'),('skillline',164,'recipeSubClass','4','Blacksmithing'),('skillline',164,'professionMask','2','Blacksmithing'),('skillline',171,'specializations','28677 28675 28672','Alchemy'),('skillline',171,'recipeSubClass','6','Alchemy'),('skillline',171,'professionMask','1','Alchemy'),('skillline',393,'professionMask','0','Skinning'),('skillline',197,'recipeSubClass','2','Tailoring'),('skillline',197,'specializations','26798 26801 26797','Tailoring'),('skillline',356,'professionMask','1024','Fishing'),('skillline',356,'recipeSubClass','9','Fishing'),('skillline',182,'professionMask','2048','Herbalism'),('skillline',773,'professionMask','4096','Inscription'),('skillline',773,'recipeSubClass','11','Inscription'),('skillline',785,'name_loc0','Pet - Wasp','Pet - Wasp'),('skillline',781,'name_loc2','Familier - diablosaure exotique','Pet - Exotic Devlisaur'),('skillline',758,'name_loc6','Mascota: Evento - Control remoto','Pet - Event - Remote Control'),('skillline',758,'name_loc3','Tier - Ereignis Ferngesteuert','Pet - Event - Remote Control'),('skillline',758,'categoryId','7','Pet - Event - Remote Control - bring in line with other pets'),('skillline',788,'categoryId','7','Pet - Exotic Spirit Beast - bring in line with other pets'),('items',33147,'class','9','Formula: Enchant Cloak - Subtlety - Class: Recipes'),('items',33147,'subClass','8','Formula: Enchant Cloak - Subtlety - Subclass: Enchanting'),('currencies',1,'description_loc0','Text that describes this item can be found here.',''),('currencies',1,'description_loc2','Un texte qui d├®crit l\'objet figure ici.',''),('currencies',1,'description_loc3','Text, der den Gegenstand beschreibt, wird hier angezeigt.',''),('currencies',1,'description_loc6','Aqu├¡ puede encontrarse el texto que describe a este objeto.',''),('currencies',1,'description_loc8','ðùð┤ðÁÐüÐî ð¢ð░Ðàð¥ð┤ð©ÐéÐüÐÅ ð¥ð┐ð©Ðüð░ð¢ð©ðÁ ð┐ÐÇðÁð┤ð╝ðÁÐéð░.',''),('currencies',61,'description_loc0','Tiffany Cartier\'s shop in Dalaran will gladly accept these tokens for unique jewelcrafting recipes.',''),('currencies',61,'description_loc2','La boutique de Tiffany Kartier, ├á Dalaran, accepte avec joie ces marques contre des dessins de joaillerie uniques.',''),('currencies',61,'description_loc3','Tiffany Cartiers Gesch├ñft in Dalaran wird diese Symbole im Tausch gegen einzigartige Juweliersrezepte dankend annehmen.',''),('currencies',61,'description_loc4','Þ¥¥µïëþäÂþÜäÞÆéÕçíÕª«┬ÀÕìíÞÆéõ║Üõ╝ܵ¼úþäµÄÑÕÅùÞ┐Öõ║øõ╗úÕ©ü´╝îÕ╣Âþö¿þ¿Çµ£ëþÜäþÅáÕ«ØÕèáÕÀÑÕø¥Úë┤µØÑõ║ñµìóÒÇé',''),('currencies',61,'description_loc6','La tienda de Tiffany Cartier en Dalaran cambiar├í gustosamente estos talismanes por recetas de joyer├¡a.',''),('currencies',61,'description_loc8','ðÆ ð╝ð░ð│ð░ðÀð©ð¢ðÁ ðóð©ÐäÐäð░ð¢ð© ðÜð░ÐÇÐéÐîðÁ, ÐçÐéð¥ ð▓ ðöð░ð╗ð░ÐÇð░ð¢ðÁ, ð▓ð░ð╝ Ðü ÐÇð░ð┤ð¥ÐüÐéÐîÐÄ ð¥ð▒ð╝ðÁð¢ÐÅÐÄÐé ÐìÐéð© ðÀð¢ð░ð║ð© ð¢ð░ Ðâð¢ð©ð║ð░ð╗Ðîð¢ÐïðÁ ÐÄð▓ðÁð╗ð©ÐÇð¢ÐïðÁ ÐìÐüð║ð©ðÀÐï.',''),('currencies',81,'description_loc0','Visit special cooking vendors in Dalaran and the capital cities to to purchase unusual cooking recipes, spices, and even a fine hat!',''),('currencies',81,'description_loc2','Rendez visite aux marchands de fournitures de cuisine ├á Dalaran et dans les autres capitales pour acheter des recettes de cuisine sp├®ciales, des ├®pices, et m├¬me une superbe toque !',''),('currencies',81,'description_loc3','Besucht besondere Kochh├ñndler in Dalaran und den Hauptst├ñdten, um ungew├Âhnliche Kochrezepte, Gew├╝rze und sogar eine gro├ƒartige M├╝tze zu kaufen!',''),('currencies',81,'description_loc4','ÚÇáÞ«┐Þ¥¥µïëþäÂõ╗ÑÕÅèÕÉäõ©¬õ©╗ÕƒÄþÜäþë╣µ«èþâ╣ÚѬõ¥øÕ║öÕòå´╝îÞ┤¡õ╣░þ¢òÞºüþÜäþâ╣ÚѬÚàìµû╣ÒÇüÚªÖµûÖõ╗ÑÕÅèÕñºÕÄ¿þÜäÕ©¢Õ¡É´╝ü',''),('currencies',81,'description_loc6','Visita a los vendedores de cocina especiales de Dalaran y de las capitales para comprar recetas de cocina poco frecuentes, especias, ┬íe incluso un bonito gorro!',''),('currencies',81,'description_loc8','ðƒð¥ÐüðÁÐéð©ÐéðÁ Ðéð¥ÐÇð│ð¥ð▓ÐåðÁð▓ ð║Ðâð╗ð©ð¢ð░ÐÇð¢Ðïð╝ð© Ðéð¥ð▓ð░ÐÇð░ð╝ð© ð▓ ðöð░ð╗ð░ÐÇð░ð¢ðÁ ð© ð┤ÐÇÐâð│ð©Ðà ÐüÐéð¥ð╗ð©Ðåð░Ðà, ÐçÐéð¥ð▒Ðï ð┐ÐÇð©ð¥ð▒ÐÇðÁÐüÐéð© ð¥Ðüð¥ð▒ÐïðÁ ð║Ðâð╗ð©ð¢ð░ÐÇð¢ÐïðÁ ÐÇðÁÐåðÁð┐ÐéÐï, Ðüð┐ðÁÐåð©ð© ð© ð┤ð░ðÂðÁ ð│ð¥ð╗ð¥ð▓ð¢ð¥ð╣ Ðâð▒ð¥ÐÇ!',''),('currencies',241,'description_loc0','Awarded for valiant acts in the Crusader\'s Coliseum.',''),('currencies',241,'description_loc2','Obtenu en r├®compense dÔÇÖactes de bravoure au colis├®e des Crois├®s.',''),('currencies',241,'description_loc3','Werden f├╝r hehre Taten im Kolosseum der Kreuzfahrer verliehen.',''),('currencies',241,'description_loc4','Þí¿Õ¢░õ¢áÕ£¿ÕìüÕ¡ùÕåøµ╝öµ¡ªÕ£║õ©¡Õ▒òþñ║þÜ䵡ªÕïçÒÇé',''),('currencies',241,'description_loc6','Otorgado por las haza├▒as en el Coliseo de los Cruzados.',''),('currencies',241,'description_loc8','ðùð░ ÐàÐÇð░ð▒ÐÇð¥ÐüÐéÐî, ð┐ÐÇð¥ÐÅð▓ð╗ðÁð¢ð¢ÐâÐÄ ð¢ð░ ÐéÐâÐÇð¢ð©ÐÇð░Ðà ðÜð¥ð╗ð©ðÀðÁÐÅ ðÉð▓ð░ð¢ð│ð░ÐÇð┤ð░.',''),('currencies',181,'description_loc0','If you can read this, you\'ve found a bug. REPORT IT!',''),('currencies',181,'description_loc2','Si vous lisez ceci, c\'est un bug. SIGNALEZ-LE !',''),('currencies',181,'description_loc3','Wenn Ihr das hier lesen k├Ânnt, habt Ihr einen Bug gefunden. MELDET IHN!',''),('currencies',181,'description_loc6','Si puedes leer esto, has encontrado un error. ┬íInforma!',''),('currencies',181,'description_loc8','ðòÐüð╗ð© ð▓Ðï ð▓ð©ð┤ð©ÐéðÁ ÐìÐéð¥ Ðüð¥ð¥ð▒ÐëðÁð¢ð©ðÁ, ÐìÐéð¥ ðÀð¢ð░Ðçð©Ðé, ÐçÐéð¥ ð▓Ðï ð¥ð▒ð¢ð░ÐÇÐâðÂð©ð╗ð© ð¥Ðêð©ð▒ð║Ðâ. ðíð¥ð¥ð▒Ðëð©ÐéðÁ ð¥ ð¢ðÁð╣!',''),('currencies',103,'description_loc0','Used to purchase powerful PvP armor and weapons.',''),('currencies',103,'description_loc2','Utilis├®s pour acheter des armures et armes de JcJ puissantes.',''),('currencies',103,'description_loc3','K├Ânnen f├╝r den Erwerb von m├ñchtigen PVP-Waffen und -R├╝stungen verwendet werden.',''),('currencies',103,'description_loc4','þ½×µèÇÕ£║þé╣µò░µÿ»ÚÇÜÞ┐çÕ£¿þ½×µèÇÕ£║µêÿµûùõ©¡ÞÄÀÞâ£ÞÇîÞÁóÕ¥ùþÜäÒÇéõ¢áÕÅ»õ╗ѵÂêÞ┤╣Þ┐Öõ║øþé╣µò░µØÑÞ┤¡õ╣░Õ╝║ÕñºþÜäÕÑûÕè▒Õôü´╝ü',''),('currencies',103,'description_loc6','Se utilizan para comprar armas y armaduras de JcJ poderosas.',''),('currencies',103,'description_loc8','ðùð░ ÐìÐéð© ð¥Ðçð║ð© ð╝ð¥ðÂð¢ð¥ ð┐ð¥ð║Ðâð┐ð░ÐéÐî ð╝ð¥Ðëð¢ð¥ðÁ ð¥ÐÇÐâðÂð©ðÁ ð© ð┤ð¥Ðüð┐ðÁÐàð© ð┤ð╗ÐÅ PvP-ÐüÐÇð░ðÂðÁð¢ð©ð╣.',''),('currencies',104,'description_loc0','Used to purchase less-powerful PvP armor and weapons.',''),('currencies',104,'description_loc2','Utilis├®s pour acheter des armures et armes de JcJ moyennement puissantes.',''),('currencies',104,'description_loc3','K├Ânnen f├╝r den Erwerb von weniger m├ñchtigen PVP-Waffen und -R├╝stungen verwendet werden.',''),('currencies',104,'description_loc4','ÞìúÞ¬ëµÿ»ÚÇÜÞ┐çÕ£¿PvPµêÿµûùõ©¡ µØÇµ¡╗µòîÕ»╣ÚÿÁÞÉÑþÜäµêÉÕæÿÞÄÀÕ¥ùþÜäÒÇéõ¢áÕÅ»õ╗Ñõ¢┐þö¿ÞìúÞ¬ëþé╣µò░Þ┤¡õ╣░þë╣µ«èþÜäþë®ÕôüÒÇé',''),('currencies',104,'description_loc6','Se utilizan para comprar armas y armaduras de JcJ menos poderosas.',''),('currencies',104,'description_loc8','ðùð░ ÐìÐéð© ð¥Ðçð║ð© ð╝ð¥ðÂð¢ð¥ ð┐ð¥ð║Ðâð┐ð░ÐéÐî ð¢ðÁ ð¥ÐçðÁð¢Ðî ð╝ð¥Ðëð¢ð¥ðÁ ð¥ÐÇÐâðÂð©ðÁ ð© ð┤ð¥Ðüð┐ðÁÐàð© ð┤ð╗ÐÅ PvP-ÐüÐÇð░ðÂðÁð¢ð©ð╣.',''),('currencies',221,'description_loc0','Used to purchase less-powerful armor and weapons.',''),('currencies',221,'description_loc2','Utilis├®s pour acheter des armures et armes de JcJ moyennement puissantes.',''),('currencies',221,'description_loc3','K├Ânnen f├╝r den Erwerb von weniger m├ñchtigen Waffen und R├╝stungen verwendet werden.',''),('currencies',221,'description_loc6','Se utilizan para comprar armas y armaduras menos poderosas.',''),('currencies',221,'description_loc8','ðùð░ ÐìÐéð© ð¥Ðçð║ð© ð╝ð¥ðÂð¢ð¥ ð┐ð¥ð║Ðâð┐ð░ÐéÐî ð¢ðÁ ð¥ÐçðÁð¢Ðî ð╝ð¥Ðëð¢ð¥ðÁ ð¥ÐÇÐâðÂð©ðÁ ð© ð┤ð¥Ðüð┐ðÁÐàð©.',''),('currencies',341,'description_loc0','Used to purchase powerful PvE armor and weapons.',''),('currencies',341,'description_loc2','Utilis├®s pour acheter des armures et armes de JcE puissantes.',''),('currencies',341,'description_loc3','K├Ânnen f├╝r den Erwerb von m├ñchtigen PVE-Waffen und -R├╝stungen verwendet werden.',''),('currencies',341,'description_loc6','Se utilizan para comprar armas y armaduras de JcE poderosas.',''),('currencies',341,'description_loc8','ðùð░ ÐìÐéð© ð¥Ðçð║ð© ð╝ð¥ðÂð¢ð¥ ð┐ð¥ð║Ðâð┐ð░ÐéÐî ð╝ð¥Ðëð¢ð¥ðÁ ð¥ÐÇÐâðÂð©ðÁ ð© ð┤ð¥Ðüð┐ðÁÐàð© ð┤ð╗ÐÅ PvE-ÐüÐÇð░ðÂðÁð¢ð©ð╣.',''),('spell',9787,'reqSpellId','9787','Weaponsmith - requires itself'),('spell',9788,'reqSpellId','9788','Armorsmith - requires itself'),('spell',10656,'reqSpellId','10656','Dragonscale Leatherworking - requires itself'),('spell',10658,'reqSpellId','10658','Elemental Leatherworking - requires itself'),('spell',10660,'reqSpellId','10660','Tribal Leatherworking - requires itself'),('spell',17039,'reqSpellId','17039','Master Swordsmith - requires itself'),('spell',17040,'reqSpellId','17040','Master Hammersmith - requires itself'),('spell',17041,'reqSpellId','17041','Master Axesmith - requires itself'),('spell',20219,'reqSpellId','20219','Gnomish Engineer - requires itself'),('spell',20222,'reqSpellId','20222','Goblin Engineer - requires itself'),('spell',26797,'reqSpellId','26797','Spellfire Tailoring - requires itself'),('spell',26798,'reqSpellId','26798','Mooncloth Tailoring - requires itself'),('spell',26801,'reqSpellId','26801','Shadoweave Tailoring - requires itself'),('spell',379,'cuFLags','1073741824','Earth Shield - hide'),('spell',17567,'cuFLags','1073741824','Summon Blood Parrot - hide'),('spell',19483,'cuFLags','1073741824','Immolation - hide'),('spell',20154,'cuFLags','1073741824','Seal of Righteousness - hide'),('spell',21169,'cuFLags','1073741824','Reincarnation - hide'),('spell',22845,'cuFLags','1073741824','Frenzied Regeneration - hide'),('spell',23885,'cuFLags','1073741824','Bloodthirst - hide'),('spell',27813,'cuFLags','1073741824','Blessed Recovery - hide'),('spell',27817,'cuFLags','1073741824','Blessed Recovery - hide'),('spell',27818,'cuFLags','1073741824','Blessed Recovery - hide'),('spell',29442,'cuFLags','1073741824','Magic Absorption - hide'),('spell',29841,'cuFLags','1073741824','Second Wind - hide'),('spell',29842,'cuFLags','1073741824','Second Wind - hide'),('spell',29886,'cuFLags','1073741824','Create Soulwell - hide'),('spell',30708,'cuFLags','1073741824','Totem of Wrath - hide'),('spell',30874,'cuFLags','1073741824','Gift of the Water Spirit - hide'),('spell',31643,'cuFLags','1073741824','Blazing Speed - hide'),('spell',32841,'cuFLags','1073741824','Mass Resurrection - hide'),('spell',34919,'cuFLags','1073741824','Vampiric Touch - hide'),('spell',44450,'cuFLags','1073741824','Burnout - hide'),('spell',47633,'cuFLags','1073741824','Death Coil - hide'),('spell',48954,'cuFLags','1073741824','Swift Zhevra - hide'),('spell',49575,'cuFLags','1073741824','Death Grip - hide'),('spell',50536,'cuFLags','1073741824','Unholy Blight - hide'),('spell',52374,'cuFLags','1073741824','Blood Strike - hide'),('spell',56816,'cuFLags','1073741824','Rune Strike - hide'),('spell',58427,'cuFLags','1073741824','Overkill - hide'),('spell',58889,'cuFLags','1073741824','Create Soulwell - hide'),('spell',64380,'cuFLags','1073741824','Shattering Throw - hide'),('spell',66122,'cuFLags','1073741824','Magic Rooster - hide'),('spell',66123,'cuFLags','1073741824','Magic Rooster - hide'),('spell',66124,'cuFLags','1073741824','Magic Rooster - hide'),('spell',66175,'cuFLags','1073741824','Macabre Marionette - hide'),('spell',54910,'cuFLags','1073741824','Glyph of the Red Lynx - hide unused glyph'),('spell',57231,'cuFLags','1073741824','Death Knight Glyph 25 - hide unused glyph'),('spell',58166,'cuFLags','1073741824','Glyph of the Forest Lynx - hide unused glyph'),('spell',58239,'cuFLags','1073741824','Glyph of the Penguin - hide unused glyph'),('spell',58240,'cuFLags','1073741824','Glyph of the Bear Cub - hide unused glyph'),('spell',58261,'cuFLags','1073741824','Glyph of the Arctic Wolf - hide unused glyph'),('spell',58262,'cuFLags','1073741824','Glyph of the Black Wolf - hide unused glyph'),('spell',60460,'cuFLags','1073741824','Glyph of Raise Dead - hide unused glyph'),('spell',54910,'skillLine1','0','Glyph of the Red Lynx - hide unused glyph'),('spell',57231,'skillLine1','0','Death Knight Glyph 25 - hide unused glyph'),('spell',58166,'skillLine1','0','Glyph of the Forest Lynx - hide unused glyph'),('spell',58239,'skillLine1','0','Glyph of the Penguin - hide unused glyph'),('spell',58240,'skillLine1','0','Glyph of the Bear Cub - hide unused glyph'),('spell',58261,'skillLine1','0','Glyph of the Arctic Wolf - hide unused glyph'),('spell',58262,'skillLine1','0','Glyph of the Black Wolf - hide unused glyph'),('spell',60460,'skillLine1','0','Glyph of Raise Dead - hide unused glyph'),('spell',54910,'iconIdAlt','0','Glyph of the Red Lynx - hide unused glyph'),('spell',57231,'iconIdAlt','0','Death Knight Glyph 25 - hide unused glyph'),('spell',58166,'iconIdAlt','0','Glyph of the Forest Lynx - hide unused glyph'),('spell',58239,'iconIdAlt','0','Glyph of the Penguin - hide unused glyph'),('spell',58240,'iconIdAlt','0','Glyph of the Bear Cub - hide unused glyph'),('spell',58261,'iconIdAlt','0','Glyph of the Arctic Wolf - hide unused glyph'),('spell',58262,'iconIdAlt','0','Glyph of the Black Wolf - hide unused glyph'),('spell',60460,'iconIdAlt','0','Glyph of Raise Dead - hide unused glyph'),('quests',9572,'questSortId','3562','Weaken the Ramparts - category Hellfire Citadel -> Hellfire Ramparts'),('quests',9575,'questSortId','3562','Weaken the Ramparts - category Hellfire Citadel -> Hellfire Ramparts'),('quests',11354,'questSortId','3562','Wanted: Nazan\'s Riding Crop - category Hellfire Citadel -> Hellfire Ramparts'),('quests',9589,'questSortId','3713','The Blood is Life - category Hellfire Citadel -> Blood Furnace'),('quests',9590,'questSortId','3713','The Blood is Life - category Hellfire Citadel -> Blood Furnace'),('quests',9607,'questSortId','3713','Heart of Rage - category Hellfire Citadel -> Blood Furnace'),('quests',9608,'questSortId','3713','Heart of Rage - category Hellfire Citadel -> Blood Furnace'),('quests',11362,'questSortId','3713','Wanted: Keli\'dan\'s Feathered Stave - category Hellfire Citadel -> Blood Furnace'),('quests',9492,'questSortId','3714','Turning the Tide - category Hellfire Citadel -> Shattered Halls'),('quests',9493,'questSortId','3714','Pride of the Fel Horde - category Hellfire Citadel -> Shattered Halls'),('quests',9494,'questSortId','3714','Fel Embers - category Hellfire Citadel -> Shattered Halls'),('quests',9495,'questSortId','3714','The Will of the Warchief - category Hellfire Citadel -> Shattered Halls'),('quests',9496,'questSortId','3714','Pride of the Fel Horde - category Hellfire Citadel -> Shattered Halls'),('quests',9497,'questSortId','3714','Emblem of the Fel Horde - category Hellfire Citadel -> Shattered Halls'),('quests',9524,'questSortId','3714','Imprisoned in the Citadel - category Hellfire Citadel -> Shattered Halls'),('quests',9525,'questSortId','3714','Imprisoned in the Citadel - category Hellfire Citadel -> Shattered Halls'),('quests',11363,'questSortId','3714','Wanted: Bladefist\'s Seal - category Hellfire Citadel -> Shattered Halls'),('quests',11364,'questSortId','3714','Wanted: Shattered Hand Centurions - category Hellfire Citadel -> Shattered Halls'),('pet',30,'expansion','1','Pet - Dragonhawk: BC'),('pet',31,'expansion','1','Pet - Ravager: BC'),('pet',32,'expansion','1','Pet - Warp Stalker: BC'),('pet',33,'expansion','1','Pet - Sporebat: BC'),('pet',34,'expansion','1','Pet - Nether Ray: BC'),('pet',37,'expansion','2','Pet - Moth: WotLK'),('pet',38,'expansion','2','Pet - Chimaera: WotLK'),('pet',39,'expansion','2','Pet - Devilsaur: WotLK'),('pet',41,'expansion','2','Pet - Silithid: WotLK'),('pet',42,'expansion','2','Pet - Worm: WotLK'),('pet',43,'expansion','2','Pet - Rhino: WotLK'),('pet',44,'expansion','2','Pet - Wasp: WotLK'),('pet',45,'expansion','2','Pet - Core Hound: WotLK'),('pet',46,'expansion','2','Pet - Spirit Beast: WotLK'),('spell',17579,'cuFlags','1610612736','Alchemy: Greater Holy Protection Potion - set: CUSTOM_UNAVAILABLE | CUSTOM_EXCLUDE_FOR_LISTVIEW'),('spell',54020,'cuFlags','1610612736','Alchemy: Transmute: Eternal Might - set: CUSTOM_UNAVAILABLE | CUSTOM_EXCLUDE_FOR_LISTVIEW'),('spell',2336,'cuFlags','1610612736','Alchemy: Elixir of Tongues - set: CUSTOM_UNAVAILABLE | CUSTOM_EXCLUDE_FOR_LISTVIEW'),('items',13460,'cuFlags','1610612736','Greater Holy Protection Potion - set: CUSTOM_UNAVAILABLE | CUSTOM_EXCLUDE_FOR_LISTVIEW'),('items',40248,'cuFlags','1610612736','Eternal Might - set: CUSTOM_UNAVAILABLE | CUSTOM_EXCLUDE_FOR_LISTVIEW'),('items',2460,'cuFlags','1610612736','Elixir of Tongues - set: CUSTOM_UNAVAILABLE | CUSTOM_EXCLUDE_FOR_LISTVIEW'),('spell',8366,'cuFlags','1610612736','Blacksmithing: Ironforge Chain - set: CUSTOM_UNAVAILABLE | CUSTOM_EXCLUDE_FOR_LISTVIEW'),('spell',2671,'cuFlags','1610612736','Blacksmithing: Rough Bronze Bracers - set: CUSTOM_UNAVAILABLE | CUSTOM_EXCLUDE_FOR_LISTVIEW'),('spell',8368,'cuFlags','1610612736','Blacksmithing: Ironforge Gauntlets - set: CUSTOM_UNAVAILABLE | CUSTOM_EXCLUDE_FOR_LISTVIEW'),('spell',9942,'cuFlags','1610612736','Blacksmithing: Mithril Scale Gloves - set: CUSTOM_UNAVAILABLE | CUSTOM_EXCLUDE_FOR_LISTVIEW'),('spell',16960,'cuFlags','1610612736','Blacksmithing: Thorium Greatsword - set: CUSTOM_UNAVAILABLE | CUSTOM_EXCLUDE_FOR_LISTVIEW'),('spell',16965,'cuFlags','1610612736','Blacksmithing: Bleakwood Hew - set: CUSTOM_UNAVAILABLE | CUSTOM_EXCLUDE_FOR_LISTVIEW'),('spell',16967,'cuFlags','1610612736','Blacksmithing: Inlaid Thorium Hammer - set: CUSTOM_UNAVAILABLE | CUSTOM_EXCLUDE_FOR_LISTVIEW'),('spell',16980,'cuFlags','1610612736','Blacksmithing: Rune Edge - set: CUSTOM_UNAVAILABLE | CUSTOM_EXCLUDE_FOR_LISTVIEW'),('spell',28244,'cuFlags','536870912','Blacksmithing: Icebane Bracers - set: CUSTOM_UNAVAILABLE'),('spell',28242,'cuFlags','536870912','Blacksmithing: Icebane Breastplate - set: CUSTOM_UNAVAILABLE'),('spell',28243,'cuFlags','536870912','Blacksmithing: Icebane Gauntlets - set: CUSTOM_UNAVAILABLE'),('spell',16986,'cuFlags','1610612736','Blacksmithing: Blood Talon - set: CUSTOM_UNAVAILABLE | CUSTOM_EXCLUDE_FOR_LISTVIEW'),('spell',16987,'cuFlags','1610612736','Blacksmithing: Darkspear - set: CUSTOM_UNAVAILABLE | CUSTOM_EXCLUDE_FOR_LISTVIEW'),('items',2867,'cuFlags','1610612736','Rough Bronze Bracers - set: CUSTOM_UNAVAILABLE | CUSTOM_EXCLUDE_FOR_LISTVIEW'),('items',6730,'cuFlags','1610612736','Ironforge Chain - set: CUSTOM_UNAVAILABLE | CUSTOM_EXCLUDE_FOR_LISTVIEW'),('items',6733,'cuFlags','1610612736','Ironforge Gauntlets - set: CUSTOM_UNAVAILABLE | CUSTOM_EXCLUDE_FOR_LISTVIEW'),('items',7925,'cuFlags','1610612736','Mithril Scale Gloves - set: CUSTOM_UNAVAILABLE | CUSTOM_EXCLUDE_FOR_LISTVIEW'),('items',12764,'cuFlags','1610612736','Thorium Greatsword - set: CUSTOM_UNAVAILABLE | CUSTOM_EXCLUDE_FOR_LISTVIEW'),('items',12769,'cuFlags','1610612736','Bleakwood Hew - set: CUSTOM_UNAVAILABLE | CUSTOM_EXCLUDE_FOR_LISTVIEW'),('items',12772,'cuFlags','1610612736','Inlaid Thorium Hammer - set: CUSTOM_UNAVAILABLE | CUSTOM_EXCLUDE_FOR_LISTVIEW'),('items',12779,'cuFlags','1610612736','Rune Edge - set: CUSTOM_UNAVAILABLE | CUSTOM_EXCLUDE_FOR_LISTVIEW'),('items',12795,'cuFlags','1610612736','Blood Talon - set: CUSTOM_UNAVAILABLE | CUSTOM_EXCLUDE_FOR_LISTVIEW'),('items',12802,'cuFlags','1610612736','Darkspear - set: CUSTOM_UNAVAILABLE | CUSTOM_EXCLUDE_FOR_LISTVIEW'),('items',22671,'cuFlags','536870912','Icebane Bracers - set: CUSTOM_UNAVAILABLE'),('items',22669,'cuFlags','536870912','Icebane Breastplate - set: CUSTOM_UNAVAILABLE'),('items',22670,'cuFlags','536870912','Icebane Gauntlets - set: CUSTOM_UNAVAILABLE'),('spell',28021,'cuFlags','1610612736','Enchanting: Arcane Dust - set: CUSTOM_UNAVAILABLE | CUSTOM_EXCLUDE_FOR_LISTVIEW'),('spell',44612,'cuFlags','1610612736','Enchanting: Enchant Gloves - Greater Blasting - set: CUSTOM_UNAVAILABLE | CUSTOM_EXCLUDE_FOR_LISTVIEW'),('spell',62257,'cuFlags','1610612736','Enchanting: Enchant Weapon - Titanguard - set: CUSTOM_UNAVAILABLE | CUSTOM_EXCLUDE_FOR_LISTVIEW'),('items',38985,'cuFlags','1610612736','Scroll of Enchant Gloves - Greater Blasting - set: CUSTOM_UNAVAILABLE | CUSTOM_EXCLUDE_FOR_LISTVIEW'),('items',44946,'cuFlags','1610612736','Scroll of Enchant Weapon - Titanguard - set: CUSTOM_UNAVAILABLE | CUSTOM_EXCLUDE_FOR_LISTVIEW'),('items',44945,'cuFlags','1610612736','Formula: Enchant Weapon - Titanguard - set: CUSTOM_UNAVAILABLE | CUSTOM_EXCLUDE_FOR_LISTVIEW'),('spell',30549,'cuFlags','1610612736','Engineering: Critter Enlarger - set: CUSTOM_UNAVAILABLE | CUSTOM_EXCLUDE_FOR_LISTVIEW'),('spell',67790,'cuFlags','1610612736','Engineering: Dimensional Folder: K3 - set: CUSTOM_UNAVAILABLE | CUSTOM_EXCLUDE_FOR_LISTVIEW'),('spell',30343,'cuFlags','1610612736','Engineering: Blue Smoke Flare - set: CUSTOM_UNAVAILABLE | CUSTOM_EXCLUDE_FOR_LISTVIEW'),('spell',30342,'cuFlags','1610612736','Engineering: Red Smoke Flare - set: CUSTOM_UNAVAILABLE | CUSTOM_EXCLUDE_FOR_LISTVIEW'),('spell',30561,'cuFlags','1610612736','Engineering: Goblin Tonk Controller - set: CUSTOM_UNAVAILABLE | CUSTOM_EXCLUDE_FOR_LISTVIEW'),('spell',30573,'cuFlags','1610612736','Engineering: Gnomish Tonk Controller - set: CUSTOM_UNAVAILABLE | CUSTOM_EXCLUDE_FOR_LISTVIEW'),('spell',12722,'cuFlags','1610612736','Engineering: Goblin Radio - set: CUSTOM_UNAVAILABLE | CUSTOM_EXCLUDE_FOR_LISTVIEW'),('spell',12904,'cuFlags','1610612736','Engineering: Gnomish Ham Radio - set: CUSTOM_UNAVAILABLE | CUSTOM_EXCLUDE_FOR_LISTVIEW'),('spell',12720,'cuFlags','1610612736','Engineering: Goblin \"Boom\" Box - set: CUSTOM_UNAVAILABLE | CUSTOM_EXCLUDE_FOR_LISTVIEW'),('spell',12900,'cuFlags','1610612736','Engineering: Mobile Alarm- set: CUSTOM_UNAVAILABLE | CUSTOM_EXCLUDE_FOR_LISTVIEW'),('items',23882,'cuFlags','1610612736','Schematic: Critter Enlarger - set: CUSTOM_UNAVAILABLE | CUSTOM_EXCLUDE_FOR_LISTVIEW'),('items',23820,'cuFlags','1610612736','Critter Enlarger - set: CUSTOM_UNAVAILABLE | CUSTOM_EXCLUDE_FOR_LISTVIEW'),('items',48933,'cuFlags','1610612736','Dimensional Folder: K3 - set: CUSTOM_UNAVAILABLE | CUSTOM_EXCLUDE_FOR_LISTVIEW'),('items',23770,'cuFlags','1610612736','Blue Smoke Flare - set: CUSTOM_UNAVAILABLE | CUSTOM_EXCLUDE_FOR_LISTVIEW'),('items',23769,'cuFlags','1610612736','Red Smoke Flare - set: CUSTOM_UNAVAILABLE | CUSTOM_EXCLUDE_FOR_LISTVIEW'),('items',23831,'cuFlags','1610612736','Goblin Tonk Controller - set: CUSTOM_UNAVAILABLE | CUSTOM_EXCLUDE_FOR_LISTVIEW'),('items',23832,'cuFlags','1610612736','Gnomish Tonk Controller - set: CUSTOM_UNAVAILABLE | CUSTOM_EXCLUDE_FOR_LISTVIEW'),('items',10585,'cuFlags','1610612736','Goblin Radio - set: CUSTOM_UNAVAILABLE | CUSTOM_EXCLUDE_FOR_LISTVIEW'),('items',10723,'cuFlags','1610612736','Gnomish Ham Radio - set: CUSTOM_UNAVAILABLE | CUSTOM_EXCLUDE_FOR_LISTVIEW'),('items',10580,'cuFlags','1610612736','Goblin \"Boom\" Box - set: CUSTOM_UNAVAILABLE | CUSTOM_EXCLUDE_FOR_LISTVIEW'),('items',10719,'cuFlags','1610612736','Mobile Alarm - set: CUSTOM_UNAVAILABLE | CUSTOM_EXCLUDE_FOR_LISTVIEW'),('spell',8387,'cuFlags','1610612736','Herbalism: Find Herbs - set: CUSTOM_UNAVAILABLE | CUSTOM_EXCLUDE_FOR_LISTVIEW'),('spell',2369,'cuFlags','1610612736','Herbalism: Herb Gathering - set: CUSTOM_UNAVAILABLE | CUSTOM_EXCLUDE_FOR_LISTVIEW'),('spell',2371,'cuFlags','1610612736','Herbalism: Herb Gathering - set: CUSTOM_UNAVAILABLE | CUSTOM_EXCLUDE_FOR_LISTVIEW'),('spell',52175,'cuFlags','1610612736','Inscription: Decipher - set: CUSTOM_UNAVAILABLE | CUSTOM_EXCLUDE_FOR_LISTVIEW'),('spell',25614,'cuFlags','1610612736','Jewelcrafting: Silver Rose Pendant - set: CUSTOM_UNAVAILABLE | CUSTOM_EXCLUDE_FOR_LISTVIEW'),('spell',32810,'cuFlags','1610612736','Jewelcrafting: Primal Stone Statue - set: CUSTOM_UNAVAILABLE | CUSTOM_EXCLUDE_FOR_LISTVIEW'),('spell',26918,'cuFlags','1610612736','Jewelcrafting: Arcanite Sword Pendant - set: CUSTOM_UNAVAILABLE | CUSTOM_EXCLUDE_FOR_LISTVIEW'),('spell',26920,'cuFlags','1610612736','Jewelcrafting: Blood Crown - set: CUSTOM_UNAVAILABLE | CUSTOM_EXCLUDE_FOR_LISTVIEW'),('items',20956,'cuFlags','1610612736','Silver Rose Pendant - set: CUSTOM_UNAVAILABLE | CUSTOM_EXCLUDE_FOR_LISTVIEW'),('items',25884,'cuFlags','1610612736','Primal Stone Statue - set: CUSTOM_UNAVAILABLE | CUSTOM_EXCLUDE_FOR_LISTVIEW'),('items',21793,'cuFlags','1610612736','Arcanite Sword Pendant - set: CUSTOM_UNAVAILABLE | CUSTOM_EXCLUDE_FOR_LISTVIEW'),('items',21780,'cuFlags','1610612736','Blood Crown - set: CUSTOM_UNAVAILABLE | CUSTOM_EXCLUDE_FOR_LISTVIEW'),('spell',10550,'cuFlags','1610612736','Leatherworking: Nightscape Cloak - set: CUSTOM_UNAVAILABLE | CUSTOM_EXCLUDE_FOR_LISTVIEW'),('spell',28224,'cuFlags','536870912','Leatherworking: Icy Scale Bracers - set: CUSTOM_UNAVAILABLE'),('spell',28222,'cuFlags','536870912','Leatherworking: Icy Scale Breastplate - set: CUSTOM_UNAVAILABLE'),('spell',28223,'cuFlags','536870912','Leatherworking: Icy Scale Gauntlets - set: CUSTOM_UNAVAILABLE'),('spell',28221,'cuFlags','536870912','Leatherworking: Polar Bracers - set: CUSTOM_UNAVAILABLE'),('spell',28220,'cuFlags','536870912','Leatherworking: Polar Gloves - set: CUSTOM_UNAVAILABLE'),('spell',28219,'cuFlags','536870912','Leatherworking: Polar Tunic - set: CUSTOM_UNAVAILABLE'),('spell',55243,'cuFlags','1610612736','Leatherworking: Bracers of Deflection - set: CUSTOM_UNAVAILABLE | CUSTOM_EXCLUDE_FOR_LISTVIEW'),('spell',19106,'cuFlags','536870912','Leatherworking: Onyxia Scale Breastplate - set: CUSTOM_UNAVAILABLE'),('items',8195,'cuFlags','1610612736','Nightscape Cloak - set: CUSTOM_UNAVAILABLE | CUSTOM_EXCLUDE_FOR_LISTVIEW'),('items',22665,'cuFlags','536870912','Icy Scale Bracers - set: CUSTOM_UNAVAILABLE'),('items',22664,'cuFlags','536870912','Icy Scale Breastplate - set: CUSTOM_UNAVAILABLE'),('items',22666,'cuFlags','536870912','Icy Scale Gauntlets - set: CUSTOM_UNAVAILABLE'),('items',22663,'cuFlags','536870912','Polar Bracers - set: CUSTOM_UNAVAILABLE'),('items',22662,'cuFlags','536870912','Polar Gloves - set: CUSTOM_UNAVAILABLE'),('items',22661,'cuFlags','536870912','Polar Tunic - set: CUSTOM_UNAVAILABLE'),('items',41264,'cuFlags','1610612736','Bracers of Deflection - set: CUSTOM_UNAVAILABLE | CUSTOM_EXCLUDE_FOR_LISTVIEW'),('items',15141,'cuFlags','536870912','Onyxia Scale Breastplate - set: CUSTOM_UNAVAILABLE'),('spell',8388,'cuFlags','1610612736','Mining: Find Minerals - set: CUSTOM_UNAVAILABLE | CUSTOM_EXCLUDE_FOR_LISTVIEW'),('spell',7636,'cuFlags','1610612736','Tailoring: Green Woolen Robe - set: CUSTOM_UNAVAILABLE | CUSTOM_EXCLUDE_FOR_LISTVIEW'),('spell',8778,'cuFlags','1610612736','Tailoring: Boots of Darkness - set: CUSTOM_UNAVAILABLE | CUSTOM_EXCLUDE_FOR_LISTVIEW'),('spell',12063,'cuFlags','1610612736','Tailoring: Stormcloth Gloves - set: CUSTOM_UNAVAILABLE | CUSTOM_EXCLUDE_FOR_LISTVIEW'),('spell',12062,'cuFlags','1610612736','Tailoring: Stormcloth Pants - set: CUSTOM_UNAVAILABLE | CUSTOM_EXCLUDE_FOR_LISTVIEW'),('spell',12068,'cuFlags','1610612736','Tailoring: Stormcloth Vest - set: CUSTOM_UNAVAILABLE | CUSTOM_EXCLUDE_FOR_LISTVIEW'),('spell',12083,'cuFlags','1610612736','Tailoring: Stormcloth Headband - set: CUSTOM_UNAVAILABLE | CUSTOM_EXCLUDE_FOR_LISTVIEW'),('spell',12087,'cuFlags','1610612736','Tailoring: Stormcloth Shoulders - set: CUSTOM_UNAVAILABLE | CUSTOM_EXCLUDE_FOR_LISTVIEW'),('spell',12090,'cuFlags','1610612736','Tailoring: Stormcloth Boots - set: CUSTOM_UNAVAILABLE | CUSTOM_EXCLUDE_FOR_LISTVIEW'),('spell',28208,'cuFlags','536870912','Tailoring: Glacial Cloak - set: CUSTOM_UNAVAILABLE'),('spell',28205,'cuFlags','536870912','Tailoring: Glacial Gloves - set: CUSTOM_UNAVAILABLE'),('spell',28207,'cuFlags','536870912','Tailoring: Glacial Vest - set: CUSTOM_UNAVAILABLE'),('spell',28209,'cuFlags','536870912','Tailoring: Glacial Wrists - set: CUSTOM_UNAVAILABLE'),('spell',36670,'cuFlags','1610612736','Tailoring: Lifeblood Belt - set: CUSTOM_UNAVAILABLE | CUSTOM_EXCLUDE_FOR_LISTVIEW'),('spell',36672,'cuFlags','1610612736','Tailoring: Lifeblood Bracers - set: CUSTOM_UNAVAILABLE | CUSTOM_EXCLUDE_FOR_LISTVIEW'),('spell',36669,'cuFlags','1610612736','Tailoring: Lifeblood Leggings - set: CUSTOM_UNAVAILABLE | CUSTOM_EXCLUDE_FOR_LISTVIEW'),('spell',36667,'cuFlags','1610612736','Tailoring: Netherflame Belt - set: CUSTOM_UNAVAILABLE | CUSTOM_EXCLUDE_FOR_LISTVIEW'),('spell',36668,'cuFlags','1610612736','Tailoring: Netherflame Boots - set: CUSTOM_UNAVAILABLE | CUSTOM_EXCLUDE_FOR_LISTVIEW'),('spell',36665,'cuFlags','1610612736','Tailoring: Netherflame Robe - set: CUSTOM_UNAVAILABLE | CUSTOM_EXCLUDE_FOR_LISTVIEW'),('spell',56048,'cuFlags','1610612736','Tailoring: Duskweave Boots - set: CUSTOM_UNAVAILABLE | CUSTOM_EXCLUDE_FOR_LISTVIEW'),('spell',31461,'cuFlags','1610612736','Tailoring: Heavy Netherweave Net - set: CUSTOM_UNAVAILABLE | CUSTOM_EXCLUDE_FOR_LISTVIEW'),('items',6243,'cuFlags','1610612736','Green Woolen Robe - set: CUSTOM_UNAVAILABLE | CUSTOM_EXCLUDE_FOR_LISTVIEW'),('items',7027,'cuFlags','1610612736','Boots of Darkness - set: CUSTOM_UNAVAILABLE | CUSTOM_EXCLUDE_FOR_LISTVIEW'),('items',10011,'cuFlags','1610612736','Stormcloth Gloves - set: CUSTOM_UNAVAILABLE | CUSTOM_EXCLUDE_FOR_LISTVIEW'),('items',10010,'cuFlags','1610612736','Stormcloth Pants - set: CUSTOM_UNAVAILABLE | CUSTOM_EXCLUDE_FOR_LISTVIEW'),('items',10020,'cuFlags','1610612736','Stormcloth Vest - set: CUSTOM_UNAVAILABLE | CUSTOM_EXCLUDE_FOR_LISTVIEW'),('items',10032,'cuFlags','1610612736','Stormcloth Headband - set: CUSTOM_UNAVAILABLE | CUSTOM_EXCLUDE_FOR_LISTVIEW'),('items',10038,'cuFlags','1610612736','Stormcloth Shoulders - set: CUSTOM_UNAVAILABLE | CUSTOM_EXCLUDE_FOR_LISTVIEW'),('items',10039,'cuFlags','1610612736','Stormcloth Boots - set: CUSTOM_UNAVAILABLE | CUSTOM_EXCLUDE_FOR_LISTVIEW'),('items',22658,'cuFlags','536870912','Glacial Cloak - set: CUSTOM_UNAVAILABLE'),('items',22654,'cuFlags','536870912','Glacial Gloves - set: CUSTOM_UNAVAILABLE'),('items',22652,'cuFlags','536870912','Glacial Vest - set: CUSTOM_UNAVAILABLE'),('items',22655,'cuFlags','536870912','Glacial Wrists - set: CUSTOM_UNAVAILABLE'),('items',30463,'cuFlags','1610612736','Lifeblood Belt - set: CUSTOM_UNAVAILABLE | CUSTOM_EXCLUDE_FOR_LISTVIEW'),('items',30464,'cuFlags','1610612736','Lifeblood Bracers - set: CUSTOM_UNAVAILABLE | CUSTOM_EXCLUDE_FOR_LISTVIEW'),('items',30465,'cuFlags','1610612736','Lifeblood Leggings - set: CUSTOM_UNAVAILABLE | CUSTOM_EXCLUDE_FOR_LISTVIEW'),('items',30460,'cuFlags','1610612736','Netherflame Belt - set: CUSTOM_UNAVAILABLE | CUSTOM_EXCLUDE_FOR_LISTVIEW'),('items',30461,'cuFlags','1610612736','Netherflame Boots - set: CUSTOM_UNAVAILABLE | CUSTOM_EXCLUDE_FOR_LISTVIEW'),('items',30459,'cuFlags','1610612736','Netherflame Robe - set: CUSTOM_UNAVAILABLE | CUSTOM_EXCLUDE_FOR_LISTVIEW'),('items',41544,'cuFlags','1610612736','Duskweave Boots - set: CUSTOM_UNAVAILABLE | CUSTOM_EXCLUDE_FOR_LISTVIEW'),('items',24269,'cuFlags','1610612736','Heavy Netherweave Net - set: CUSTOM_UNAVAILABLE | CUSTOM_EXCLUDE_FOR_LISTVIEW');
+/*!40000 ALTER TABLE `aowow_setup_custom_data` 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 */;
+/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;
+
+-- Dump completed on 2025-09-22 23:29:16
diff --git a/setup/sql/03-db_initial_articles.sql b/setup/sql/03-db_initial_articles.sql
new file mode 100644
index 00000000..5450d9e3
--- /dev/null
+++ b/setup/sql/03-db_initial_articles.sql
@@ -0,0 +1,35 @@
+-- MariaDB dump 10.19 Distrib 10.4.32-MariaDB, for Win64 (AMD64)
+--
+-- Host: localhost Database: aowow
+-- ------------------------------------------------------
+-- Server version 10.4.32-MariaDB
+
+/*!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_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;
+/*!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' */;
+/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;
+
+--
+-- Dumping data for table `aowow_articles`
+--
+
+LOCK TABLES `aowow_articles` WRITE;
+/*!40000 ALTER TABLE `aowow_articles` DISABLE KEYS */;
+INSERT INTO `aowow_articles` VALUES (13,4,0,NULL,0,2,'[b][color=c4]Rogues[/color][/b] are a leather-clad melee class capable of dealing large amounts of damage to their enemies with very fast attacks. They are masters of stealth and assassination, passing by enemies unseen and striking from the shadows, then escaping from combat in the blink of an eye.\r\n\r\nThey are capable of using poisons to cripple their opponents, massively weakening them in battle. Rogues have a powerful arsenal of skills, many of which are strengthened by their ability to stealth and to incapacitate their victims.\r\n[ul]\r\n[li]Rogues can use a wide variety of melee weapons, such as daggers, fist weapons, one-handed maces, one-handed swords and one-handed axes.[/li]\r\n[li]By coating their weapons with [url=items=0.-3&filter=na=poison;ub=4]poison[/url] rogues can severely cripple or weaken their enemies.[/li]\r\n[li]When using [spell=1784] rogues will be unseen except by the most perceptive enemies.[/li]\r\n[/ul]'),(14,1,0,NULL,0,2,'[b]Overview:[/b] The [b]humans[/b] are the most populous and the youngest race in Azeroth. The humans have become the [i]de facto[/i] leaders of the Alliance, with their youthful ambitions and resilience.\n\n[b]Capital City:[/b] The human seat of power is in the rebuilt city of [zone=1519].\n\n[b]Starting Zone:[/b] Humans begin questing in [zone=12].\n\n[b]Mounts:[/b] [npc=384] sells armoried ponies in Stormwind, and [npc=33307] at the Argent Tournament has a few distinct models.'),(13,1,0,NULL,0,2,'[b][color=c1]Warriors[/color][/b] are a very powerful class, with the ability to tank or deal significant melee damage. The warrior\'s Protection tree contains many talents to improve their survivability and generate threat versus monsters. Protection warriors are one of the main tanking classes of the game.\n\nThey also have two damage-oriented talent trees - [icon name=ability_rogue_eviscerate][url=spells=7.1.26]Arms[/url][/icon] and [icon name=ability_warrior_innerrage][url=spells=7.1.256]Fury[/url][/icon], the latter of which includes the talent [spell=46917], which allows the warrior to wield two two-handed weapons at the same time! They are capable of strong melee AoE damage with spells such as [spell=845], [spell=1680], [spell=46924]. A warrior fights while in a specific [i]stance[/i], which grants him bonuses and access to different sets of abilities. He will use [spell=71] for tanking, and [spell=2457] or [spell=2458] for melee DPS.\n\n[ul]\n[li]All warriors can buff their raid or group by using a [i]shout[/i], [spell=6673] or [spell=469], and Fury warriors can provide the passive buff [spell=29801] which significantly increases the melee and ranged critical strike chance of his allies.[/li]\n[li]Warriors start out with only [spell=2457] at first, but learn [spell=71] at level 10 and [spell=2458] at level 30.[/li]\n[li]Warriors have numerous useful methods of getting to their target in a hurry! All warriors can use [spell=100] or [spell=20252] to reach an enemy and Protection warriors have [spell=3411], which allows them to intercept a friendly target and protect them from an attack.[/li]\n[/ul]'),(13,2,0,NULL,0,2,'[b][color=c2]Paladins[/color][/b] bolster their allies with holy auras and blessing to protect their friends from harm and enhance their powers. Wearing heavy armor, they can withstand terrible blows in the thickest battles while healing their wounded allies and resurrecting the slain. In combat, they can wield massive two-handed weapons, stun their foes, destroy undead and demons, and judge their enemies with holy vengeance. Paladins are a defensive class, primarily designed to outlast their opponents.\n\nThe paladin is a mix of a melee fighter and a secondary spell caster. The paladin has a great deal of group utility due to the paladin\'s healing, blessings, and other abilities. Paladins can have one active aura per paladin on each party member and use specific blessings for specific players. Paladins are pretty hard to kill, thanks to their assortment of defensive abilities. They also make excellent tanks using their [spell=25780] ability.\n\n[ul]\n[li]Can effectively heal, tank, and deal damage in melee.[/li]\n[li]Has a wide selection of [url=spells=7.2&filter=na=blessing]Blessings[/url], [url=spells=7.2&filter=na=aura]Auras[/url], and other buffs.[/li]\n[li]Is the only class with access to a true invulnerability spell: [spell=642][/li]\n[/ul]'),(14,2,0,NULL,0,2,'[b]Overview:[/b] The [b]orcs[/b] were originally a race of noble savages, residing on the world of Draenor. Unfortunately, The Burning Legion made use of them in an attempt to conquer Azeroth—they were infected with the daemonic blood of Mannoroth the Destructor, driven mad, and turned upon both the Draenei and the denizens of Azeroth. After losing the Second War, they were cut off from the corrupting influence of Mannoroth, and began to return to their shamanistic roots. Now, under the leadership of their new Warchief, the orcs are carving out a home for themselves in Azeroth.\n\n[b]Capital City:[/b] The orcs now reside in the city of [zone=1637], named after the deceased Orgrim Doomhammer, former Warchief of the Horde.\n\n[b]Starting Zone:[/b] Orcs begin questing in [zone=14].\n\n[b]Mounts:[/b] [npc=3362] in Orgrimmar sells a variety of wolves; [npc=33553] sells a few distinctive mounts at the Argent Tournament.'),(13,3,0,NULL,0,2,'[b][color=c3]Hunters[/color][/b] are a very unique class in World of Warcraft. They are the sole non-magical ranged damage-dealers, fighting with bows and guns. Hunters have a number of different kinds of shots and stings, which can be used to debuff an enemy, and are capable of laying traps to deal damage or otherwise slow/incapacitate their enemy.\n\nA hunter will also tame his very own [url=pets]pet[/url] to aid them in combat. While they are not the only class which can use pet minions, the hunter\'s pet is unique in that each species has a particular type of talent tree, which the hunter can use to distribute points into various skills and passive abilities.\n\nIn addition, each species has a unique special ability. Hunters can seek out the most desirable pets based on their appearances or abilities, and if they spec deep enough into the [icon name=ability_hunter_beasttaming][url=spells=7.3.50]Beast Mastery[/url][/icon] tree they gain access to special, \"exotic\" beasts such as [pet=46] or [pet=39]!\n\n[ul]\n[li]Hunters have access to 23 (32 if [icon name=ability_hunter_beasttaming][url=spells=7.3.50]Beast Mastery[/url][/icon]) different [url=pets]species of pets[/url], featuring over 150 different appearances![/li]\n[li]Hunters have a number of survival-oriented skills which they can use to escape or avoid potential danger, such as [spell=5384] and [spell=781].[/li]\n[li][icon name=ability_hunter_swiftstrike][url=spells=7.3.51]Survival[/url][/icon] hunters can spec down the tree into [spell=53292], which allows them to provide the [spell=57669] buff to their party and raid members.[/li]\n[/ul]'),(13,5,0,NULL,0,2,'[b][color=c5]Priests[/color][/b] are commonly considered one of the standard healing classes in World of Warcraft, as they have two talent specs that can be used to heal quite effectively.\n\nTheir [icon name=spell_holy_holybolt][url=spells=7.5.56]Holy[/url][/icon] tree includes talents which strongly boost the healing done to their allies, including spells that can be used to heal multiple players at once, such as [spell=48089]. The [icon name=spell_holy_wordfortitude][url=spells=7.5.613]Discipline[/url][/icon] tree, while still capable of significant raw healing output, focuses primarily on damage absorption and mitigation through use of [spell=48066] and procced shielding effects. Priests are also capable of very powerful ranged damage with their unique [icon name=spell_shadow_shadowwordpain][url=spells=7.5.78]Shadow[/url][/icon] abilities, and upon entering [spell=15473] will see a significant increase in their shadow damage while losing the ability to cast any Holy spells.\n\n[ul]\n[li]While the [icon name=spell_holy_wordfortitude][url=spells=7.5.613]Discipline[/url][/icon] talent tree is commonly used for healing, it also contains some powerful talents that can boost the priest\'s Holy damage, though [icon name=spell_shadow_shadowwordpain][url=spells=7.5.78]Shadow[/url][/icon] spells and abilities should be used primarily for DPS.[/li]\n[li]Priests provide of the most appreciated buffs in the game - [spell=48161], which grants an indispensable stamina buff to everyone in the raid. They can also buff both [spell=48073] and [spell=48169]![/li]\n[li]Shadow priests are an excellent utility class for any raid, providing the much-loved [spell=57669] buff to boost mana regeneration and can even heal their own party with [spell=15286]![/li]\n[/ul]'),(13,6,0,NULL,0,2,'Introduced in the Wrath of the Lich King expansion, [b][color=c6]Death Knights[/color][/b] are World of Warcraft\'s first hero class. Death knights start at level 55 in a special, instanced zone unreachable by any other class: Acherus, the Ebon Hold, located in [zone=4298]. Here they will earn their talent points as quest rewards and even get a special summoned mount, the [spell=48778]!\n\nDeath knights have multiple very strong damage dealing options, as each of their talent trees can be specced to perform exceptionally well with a variety of melee abilities, spells and damage-over-time dealing diseases. They are also very capable tank classes, with both their Blood and Frost trees providing unique options - [icon name=spell_deathknight_bloodboil][url=spells=7.6.770]Blood[/url][/icon] dealing more with self-healing abilities and [icon name=spell_frost_frostnova][url=spells=7.6.771]Frost[/url][/icon] providing significant damage mitigation and strong AoE damage.\n\nDeath knights fight with a special buff active called a [i]presence[/i] (similar to a warrior\'s stances) which provides special bonuses to their roles. Death knights utilize a unique power system, with most spells costing either Runes, which are replenished throughout battle, or Runic Power, which can be generated by various abilities.\n\n[ul]\n[li][icon name=spell_deathknight_armyofthedead][url=spells=7.6.772]Unholy[/url][/icon] death knights can spec into [spell=52143], which makes their summoned Ghoul minion a permanent pet to aid in battle![/li]\n[li]The death knight class has its own special weapon enchanting ability called [spell=53428], which replaces the need for conventional weapon enchants.[/li]\n[li]Death knights are a very unique damage-dealing class in that their damage is dealt by both melee abilities [i]and[/i] spells![/li]\n[/ul]'),(13,7,0,NULL,0,2,'[b][color=c7]Shamans[/color][/b] master elemental and nature magics and bring the most potential buffs to any group in the form of totems. A shaman can summon one totem of each element - earth, fire, air, and water - which appears at the shaman\'s feet and provides a buff to anyone in the shaman\'s party or raid within range of it. Some shaman totems, notably the fire ones, also do damage to opponents. The trick to playing any type of shaman is knowing which totems to cast under which circumstances to maximize the group\'s damage output and survivability.\n\nShamans are primarily spellcasters, although an [icon name=spell_nature_lightningshield][url=spells=7.7.373]Enhancement[/url][/icon] shaman likes to get close and personal and do damage within melee range. An enhancement shaman learns to [spell=30798] weapons and can use [spell=51533] to summon a pair of Spirit Wolves to aid in battle. Despite being primarily melee, [icon name=spell_nature_lightningshield][url=spells=7.7.373]Enhancement[/url][/icon] shamans can still gain some benefit from spellpower and can cast instant [spell=403] or heals with [spell=51530]. \n\n[icon name=spell_nature_lightning][url=spells=7.7.375]Elemental[/url][/icon] shamans stand back and cast fire and lightning spells to deal great amounts of damage. They can push back enemies with [spell=51490] and root all enemies in an area with[spell=51486]. They also bring [icon name=spell_fire_totemofwrath][url=spell=57722]Totem of Wrath[/url][/icon] and [spell=51470] as amazing spellcaster raid buffs. A shaman that choses [icon name=spell_nature_magicimmunity][url=spells=7.7.374]Restoration[/url][/icon] gains improved healing spells and can be a great raid or tank healer. Resto shamans are known for their powerful [spell=1064] ability and for providing a [spell=16190] to help their party\'s mana restoration. They also gain a powerful [spell=974], can use [spell=51886] to remove curses, and have an instant-cast direct heal plus heal over time effect called [spell=61295].\n\n[ul]\n[li]There are over twenty different totems a shaman can learn![/li]\n[li]Shamans can cast [spell=2825] (or [spell=32182]) to boost the entire group\'s damage and healing. This buff is unique and oft sought after for a raid group.[/li]\n[li]A shaman can turn into a [spell=2645] at level 16 and can even make it instant cast with [spell=16287]. This spell can be used in combat, but not indoors.[/li]\n[li]Shamans can only have one elemental shield - [spell=324] or [spell=52127] - on at a time. [spell=974], if the shaman knows it, can be cast on another player.[/li]\n[/ul]'),(13,8,0,NULL,0,2,'[b][color=c8]Mages[/color][/b] wield the elements of fire, frost, and arcane to destroy or neutralize their enemies. They are a robed class that excels at dealing massive damage from afar, casting elemental bolts at a single target, or raining destruction down upon their enemies in a wide area of effect. Mages can also augment their allies\' spell-casting powers, summon food or drink to restore their friends, and even travel across the world in an instant by opening arcane portals to distant lands.\n\nWhen seeking someone to introduce monsters to a world of pain, the Mage is a good choice. With their elemental and arcane attacks, it\'s a safe bet something they can do won\'t be resisted by your chosen enemy. Damage is the name of the Mage game, and they do it well. Their arsenal includes some powerful buffs, debuffs, stuns, and snares, enabling them to dictate the terms of any fight.\n\n[ul]\n[li]Can [spell=42956] to restore their allies\' health and mana.[/li]\n[li]Are the only class that can create portals to transport other players. They cannot, however, summon players [i]from[/i] a distant location - that\'s a [icon name=class_warlock][color=c9]Warlock\'s[/color][/icon] job![/li]\n[li]Mages who use [item=50045] can have a permanent water elemental pet![/li]\n[/ul]'),(13,9,0,NULL,0,2,'[b][color=c9]Warlocks[/color][/b] are masters of the demonic arts. Clothed in demonic styled cloth, they excel in using curses, firing bolts of fire or shadow, and summoning demons to help them in combat. Warlocks, while being excellent spell casters, also excel in supporting fellow allies by summoning other players or using ritual magics to conjure stones imbued with the power to heal.\r\n\r\nA warlock has very powerful abilities that, if used correctly, make them a very formidable opponent. Using their curses in combination with direct damage spells, Warlocks wreak havoc and destruction.\r\n\r\n[ul]\r\n[li]Can use a [spell=698] to summon another player to the portals location.[/li]\r\n[li]Are able to conjure [icon name=inv_stone_04][url=item=5509]Healthstones[/url][/icon] that have the ability to heal the user.[/li]\r\n[li]Can use curses on enemies to [url=spell=47865]weaken[/url] them or [url=spell=47864]damage[/url] them.[/li]\r\n[/ul]'),(13,11,0,NULL,0,2,'[b][color=c11]Druids[/color][/b] are World of Warcraft\'s \"jack of all trades\" class -- that is, capable of performing in a variety of different roles and as such have one of the most varied playstyles. A druid can act as a healer, melee DPS, ranged DPS or a tank, utilizing a variety of [i]shapeshifting[/i] forms. As a druid levels up, he is able to learn new, powerful forms which he can cast to change into different creatures to suit their roles.\n\nAt lower levels, a druid will heal or ranged DPS in his caster form, but at later levels players who spec into the specialized trees will gain access to two special shapeshift forms for each different role.\n\nHealing druids will learn [spell=33891], which reduces the mana cost of their healing spells and grants a passive healing aura to their allies. Their ranged damage-dealing counterparts will learn [spell=24858], increasing their armor and granting a spell critical aura to their allies. There are also two feral form druid forms -- the mighty [spell=5487] (and at later level, [spell=9634]), a tanking-oriented form which provides additional armor and health and grants access to an arsenal of threat-building and damage mitigation abilities, and the rogue-like [spell=768] which is capable of significant melee DPS.\n\n[ul]\n[li]Druids learn their different forms through questing or training. Some shapeshifts are only learned via talents.[/li]\n[li]There are some shapeshifts that all druids can learn. [spell=5487] is obtained at level 10, [spell=1066] and [spell=783] at level 16, [spell=768] at level 20 and [spell=9634] at level 40.[/li]\n[li]Druids even have their own flying travel form! [spell=33943] can be trained at level 60, and [spell=40120] at level 71 provided the player has already trained [spell=34091].[/li]\n[li]Some druid shapeshifts are obtained via talents only - [spell=24858] can be obtained at level 40 when a player specs deep into the [icon name=spell_nature_starfall][url=spells=7.11.574]Balance[/url][/icon] tree, and [spell=33891] at level 50 after speccing deep into [icon name=spell_nature_healingtouch][url=spells=7.11.573]Restoration[/url][/icon].[/li]\n[li]Druids have their own, class-specific teleport ability that allows them to travel to and from [zone=493], which is handy when needing to train![/li]\n[li]Because feral druids do not actually swing weapons while in shapeshift forms, they instead gain a special statistic from any melee weapon they equip called \"feral attack power.\" This stat is a conversion of a weapon\'s DPS (damage per second) into an attack power-granting statistic which affects the cat or bear\'s damage output.[/li]\n[/ul]'),(14,3,0,NULL,0,2,'[b]Overview:[/b] The [b]dwarves[/b] are a hardy race, hailing from Khaz Modan in the Eastern Kingdoms. Rumor has it they are descended from the Titans. There are three main clans of dwarves vying for power in Ironforge: the Bronzebeards, Wildhammers, and Dark Irons.\n\n[b]Capital City:[/b] The dwarves make their home in their ancestral seat of [zone=1537].\n\n[b]Starting Zone:[/b] Dwarves begin in [zone=1].\n\n[b]Mounts:[/b] [npc=1261] by the Amberstill Ranch sells rams, as well as [npc=33310] at the Argent Tournament.'),(14,4,0,NULL,0,2,'[b]Overview:[/b] The [b]night elves[/b] are an ancient and mysterious race. They lived in Kalimdor for thousands of years, undisturbed until the world tree was sacrificed to halt the advance of the Burning Legion prior to the events of World of Warcraft.\n\n[b]Capital City:[/b] The night elf capital city is [zone=1657], situated in the branches of the world tree itself.\n\n[b]Starting Zone:[/b] Night Elves begin in [zone=141], learning about the recent political changes in Darnassus.\n\n[b]Mounts:[/b] [npc=4730] in Darnassus sells a variety of nightsabers, as well as [npc=33653] at the Argent Tournament.'),(14,5,0,NULL,0,2,'[b]Overview:[/b] When the [b]undead[/b] scourge initially swept across Azeroth, they converted a number of members of the Alliance to the undead. When the combined forces of the orcs, elves, trolls, dwarves and humans began to fight back, though, [npc=36597]\'s hold on his forces began to weaken. A small faction of humans, known as the Forsaken, broke free of the Lich King\'s control.\n\nNow, free of the bonds of servitude as well as the troublesome emotions and connections of their human lives, the Forsaken have found a new home—with the Horde.\n\n[b]Capital City:[/b] The Forsaken reside in the [zone=1497], underneath the ruins of the former human city of Lordaeron.\n\n[b]Starting Zone:[/b] [zone=85] is the starting zone for Forsaken players--they are raised as second-generation Forsaken by val\'kyr and experience Sylvanas\' menacing new agenda firsthand.\n\n[b]Mounts:[/b] [npc=4731] in Tirisfal Glades sells numerous undead horses; [npc=33555] at the Argent Tournament sells a few distinct models.'),(14,6,0,NULL,0,2,'[b]Overview:[/b] The [b]tauren[/b], a race with deep shamanistic roots, are longtime residents of Kalimdor. They have a deep and abiding love of nature, and the vast majority of them worship a deity known as the Earth Mother. \n\n[b]Capital City:[/b] The tauren reside in [zone=1638].\n\n[b]Starting Zone:[/b] Tauren begin questing in [zone=215].\n\n[b]Mounts:[/b] [npc=3685] sells numerous kodo mounts; [npc=33556] at the Argent Tournament sells a few distinctive models.'),(14,7,0,NULL,0,2,'[b]Overview:[/b] The [b]gnomes[/b] are a quirky race, obsessed with gadgets and technology. They originally come from the city of [zone=721], which was destroyed by [npc=7937] in an attempt to save it from an invading army of troggs.\n\n[b]Capital City:[/b] The gnomes now make their home in [zone=1537]; they have made efforts to retake their beloved former city with [achievement=4786].\n\n[b]Starting Zone:[/b] Gnomes begin in [zone=1], but they have a very different quest sequence from Dwarves, covering Gnomeregan.\n\n[b]Mounts:[/b] [npc=7955] in Dun Morogh sells numerous mechanostriders, as well as [npc=33650] at the Argent Tournament.'),(14,8,0,NULL,0,2,'[b]Overview:[/b] While there are many different tribes of [b]trolls[/b] scattered across Azeroth, only the [url=?faction=530]Darkspear Tribe[/url] has ever sworn allegiance to the Horde. The trolls originally lived in the Broken Isles, but were overrun by naga and murlocs and driven from their home. The orcs, led by [npc=4949], saved the Darkspear tribe from certain destruction and offered them amnesty among the Horde. In return, the Darkspear tribe swore fealty to the orcish warchief.\n\n[b]Capital City:[/b] The Darkspear Trolls live now in the Horde capital of [zone=1637].\n\n[b]Starting Zone:[/b] Trolls begin questing in [b]Echo Isles[/b].\n\n[b]Mounts:[/b] [npc=7952] in Sen\'jin Village sells numerous raptors; [npc=33554] at the Argent Tournament sells a few distinctive models.'),(14,10,0,NULL,0,2,'[b]Overview:[/b] The [b]blood elves[/b] are a proud, haughty race, joining the Horde in Burning Crusade. They represent a faction of former high elves, split off from the rest of elven society; they are also survivors of Arthas\' assault on Silvermoon. Blood elves are fully dependent on magic, having revelled in its power for so long that they suffer horrible withdrawal if it were to be taken away.\n\n[b]Capital City:[/b] The blood elves have rebuilt [zone=3487].\n\n[b]Starting Zone:[/b] [zone=3430] is the starting zone for Blood Elves.\n\n[b]Mounts:[/b] [npc=16264] in Eversong Woods sells numerous hawkstriders; [npc=33557] at the Argent Tournament sells a few unique models.'),(14,11,0,NULL,0,2,'[b]Overview:[/b] The [b]Draenei[/b] are followers of the Naaru and worshipers of the Holy Light. They originally hail from the distant world of Argus, fleeing after Sargeras tried to corrupt them. They then settled on the Orcish homeworld of Draenor, where after a period of peace, they were brutally murdered during Guldan\'s corruption of the Orcs. Finally they settled in Azeroth, to seek aid in their battle against the Burning Legion. Draenei were introduced in the Burning Crusade expansion.\n\n[b]Capital City:[/b] The Draenei have the seat of their power in the ruins of their once-great ship, [zone=3557].\n\n[b]Starting Zone:[/b] [zone=3524] and [zone=3525] cover the attempts of the Draenei to settle on their new island and deal with the inherent corruption present.\n\n[b]Mounts:[/b] [npc=17584] sells a variety of Elekks, as well as [npc=33657] at the Argent Tournament.'),(8,21,0,NULL,0,2,'[minibox]\n[h2]Steamwheedle Cartel[/h2]\n[b]Booty Bay[/b]\n[faction=577]\n[faction=369]\n[faction=470]\n[/minibox]\n\n\n[b]Booty Bay[/b] is a large pirate town nestled into the cliffs surrounding a beautiful blue lagoon on the southern tip of [zone=33]. The city is entered by traversing through the bleached-white jaws of a giant shark.\n\nRun by the Blackwater Raiders who are closely associated with the Steamwheedle Cartel, the port offers facilities to any traveller passing through, regardless of their faction. Combined with the world renowned Salty Sailor Tavern, [event=15], numerous profession trainers, and vendors that sell everything from pets to diamond rings, it is one of the most popular locations in Azeroth.\n\n[npc=2496], ruler of this city, is hiring all the help he can get against the pesky [faction=87] and other threats of the city. He resides, together with the leader of the Blackwater Raiders, [npc=2487], at the top of the inn of Booty Bay.\n\nDue to the boat route from Booty Bay to Ratchet, players of all level ranges (mostly Horde, if lower level) can be expected to be found going about their business, although frequent visitors will more than likely fit in the 35 - 45 range. The quests available from the locals reflect this range nicely.\n\nThe water there occasionally has floating wreckages and schools of fish. The schools that are found most often are [item=6359], [item=6358], and [item=13422]. Fishing in the floating wreckages will also give you very high chances of fishing out chests and items, making Booty Bay an ideal place for fishing.\n\n[h3]Reputation[/h3]\nMost of the quests to raise reputation with Booty Bay are located in The Cape of Stranglethorn. Having a friendly or higher reputation will make the guards help you in case of initiated violence against you.\n\nIf you are Hated with Booty Bay, you can do the repeatable quest [quest=9259] to get back to Neutral.'),(8,47,0,NULL,0,2,'[b]Ironforge[/b] is the faction associated with the capital city of the dwarves, [zone=1537]. [npc=2784] rules his kingdom of Khaz Modan from his throne room within the city, and the [npc=7937], leader of the gnomes, has temporarily had to settle down in Tinker Town after the recent fall of the gnome city [zone=133].\n\n[h3]History[/h3]\nIronforge is the ancient home of the dwarves. A marvel to the dwarves\' skill at shaping rock and stone, Ironforge was constructed in the very heart of the mountains, an expansive underground city home to explorers, miners, and warriors. Massive doors of rock protect the city in times of war, and lava from the mountain itself is redirected and distributed for heat, energy and smithing purposes. Before the Dark Iron Clan was banished from the city, eventually leading to the War of the Three Hammers, Ironforge was the commercial and social center of all the dwarven clans. It is now home to the Bronzebeard Clan. Many dwarven strongholds fell during the Second War between the Horde and the Alliance of Lordaeron, but the mighty city of Ironforge, nestled in the wintry peaks of [zone=1] and protected by its great gates, was never breached by the invading Horde.\n\nRelatively recently, Ironforge also became home to the Gnomeregan refugees. After the Third War, the gnomish city of Gnomeregan became overrun by troggs. Since then, a number of gnomes have settled in Ironforge, converting an area of that city to their liking, an area now known as Tinker Town.\n\nIronforge is one of most populated cities in the world, coming after the human city of [zone=1519], and housing 20,000 people.\n\nWhile the Alliance has been weakened by recent events, the dwarves of Ironforge, led by King Magni Bronzebeard, are forging a new future in the world.[h3]Reputation[/h3]\n[npc=14723] has the repeatable cloth reputation quests. As a reward for being exalted with Ironforge, non-dwarf players are able to ride [url=?items=15.5&filter=na=Ram;cr=93:92;crs=2:1;crv=0:0]rams[/url].\n\nSurrounding zones [zone=1], [zone=38] and [zone=11] contain the most quests for gaining reputation with Ironforge.'),(8,54,0,NULL,0,2,'[b]Gnomeregan Exiles[/b] is the faction of gnomes who fled from their home, [zone=133] in [zone=1]. It was destroyed by the [url=?npcs=7&filter=na=Trogg]Trogg[/url] after a toxic invasion. Now a member of the Alliance, most are located in the Tinkertown section of the neighboring city [zone=1537], including leader [npc=7937].\n\n[h3]History[/h3]\nIt has been speculated that gnomes were formed as robots by the Titans, due to their inquisitive nature and technical skills.\n\nGnomes were an underground race of tinkers, residing in Gnomeregan until the troggs destroyed it. In this war, over 80% of the gnomish population was lost.\n\n[h3]Reputation[/h3]\n[npc=14724] has the repeatable cloth reputation quests. As a reward for being exalted with Ironforge, non-gnome dwarf players are able to ride [url=?items=15.5&filter=na=Mechanostrider;cr=93:92;crs=2:1;crv=0:0]mechanostriders[/url].\nSurrounding zone [zone=1] contain the most quests for gaining reputation with the Gnomeregan Exiles.'),(8,59,0,NULL,0,2,'The [b]Thorium Brotherhood[/b] are an elite group of craftsmen who can reveal a number of epic recipes if you gain enough faction reputation with them. All players start off at Neutral reputation with them.\n\n[h3]History[/h3]\n\nThe [zone=51] is home to a group of exceptionally stout dwarves who have split from the Dark Iron Clan. On the cliffs overlooking the region called the Cauldron, in the far north of the Searing Gorge, the dwarves of the Thorium Brotherhood have established a base of operations, Thorium Point. From here, they keep a close eye on the Dark Iron dwarves\' activities in the Searing Gorge and beyond. Adventurers seeking out Thorium Point will find that the dwarves of the Thorium Brotherhood hold great rewards for those who aid them in their never ending struggle against their former brethren.\n\nThe Thorium Brotherhood comprises many exceptionally talented craftsmen, and the blacksmiths of the Brotherhood are rumored to be among the finest Azeroth has ever seen. They possess the knowledge required to make the arms and armaments of [npc=11502], the Fire Lord, but lack the manpower to obtain the materials required for the crafting. It is rumored that one member of the Thorium Brotherhood has been empowered to trade the dwarves\' fabled recipes and plans with those who can prove their loyalty to the Brotherhood. Of course, proving one\'s loyalty at some point may include venturing to the heart of the [zone=2717], the domain of Ragnaros, the Fire Lord himself, to supply the dwarves with the rare raw materials found there. A daunting task, no doubt, but gaining access to the Thorium Brotherhood\'s secrets should prove to be a reward well worth the effort.\n\n[h3]Reputation[/h3]\n\n[b]Neutral to Friendly[/b]\n\n[ul]\n[li]Turn in [item=18944], [item=3857] and either [item=4234], [item=3575], or [item=3356] to [npc=14624].[/li][/ul]\n[b]Friendly to Honored[/b]\n\n[ul]\n[li]Turn in [item=18945] to Master Smith Burninante.[/li][/ul]\n[b]Honored to Exalted[/b]\n\n[ul]\n[li]Turn in [item=11370] to [npc=12944].[/li]\n[li]Turn in [item=17012] to Lokhtos Darkbargainer.[/li]\n[li]Turn in [item=17010] to Lokhtos Darkbargainer.[/li]\n[li]Turn in [item=17011] to Lokhtos Darkbargainer.[/li]\n[li]Turn in [item=11382] to Lokhtos Darkbargainer.[/li][/ul]'),(8,68,0,NULL,0,2,'[b]Undercity[/b] is the faction for the capital city of the Forsaken Undead, [zone=1497], ruled by Sylvanas Windrunner. It is located in [zone=85], at the northern edge of the Eastern Kingdoms. The city proper is located under the ruins of the historical City of Lordaeron. To enter it, you will walk through the ruined outer defenses of Lordaeron and the abandoned throneroom, until you reach one of three elevators guarded by two abominations.\n\n[h3]History[/h3]\nThe Undercity was originally simply a system of sewers, crypts, and catacombs beneath the Capital City of Lordaeron. After the city was destroyed by the Scourge, Arthas had the underground warren expanded and rebuilt. He originally intended for the Undercity to be his seat of power, from which he would rule the Plaguelands. However, shortly after the Third War ended, Arthas was forced to return to Northrend and save the Lich King. In his absence, [npc=10181] and her rebel Undead captured the ruins of the city. Soon after, she discovered the massive underground fortress, and decided to establish it as the main base of operations for the Undead Forsaken.\n\n[h3]Reputation[/h3]\n[npc=14729] has the Undercity repeatable cloth quests used by non-Undead Horde players to obtain the right to ride [url=?items=15.5&filter=na=Skeletal;cr=93:92;crs=2:1;crv=0:0]skeletal horses[/url] at exalted.\n\nSurrounding zones [zone=267], [zone=130], and Tirisfal Glades have the most quests to earn reputation with Undercity.'),(8,69,0,NULL,0,2,'[b]Darnassus[/b] is the faction associated with [zone=1657], the capital city of the Night Elves. The high priestess, [npc=7999], resides in the Temple of the Moon, surrounded by other sisters of Elune. In the Cenarion Enclave, the [npc=3516] leads the [faction=609], often in direct opposition to his fellow druids in [zone=493] and Tyrande herself.\n\n[h3]History[/h3]\nIn the aftermath of the Third War, the night elves had to adjust to their mortal existence. Such an adjustment was far from easy, and there were many night elves who could not adjust to the prospects of aging, disease and frailty. Seeking to regain their immortality, a number of wayward druids conspired to plant a special tree that would reestablish a link between their spirits and the eternal world.\n\nWith [npc=15362] missing, Fandral Staghelm - the leader of those who wished to plant the new World Tree - became the new Arch-Druid. In no time at all, he and his fellow druids had forged ahead and planted the great tree, [zone=141], off the stormy coasts of northern Kalimdor. Under their care, the tree sprouted up above the clouds. Among the twilight boughs of the colossal tree, the wondrous city of Darnassus took root. However, the tree was not consecrated with nature\'s blessing and soon fell prey to the corruption of the Burning Legion. Now the wildlife and even the limbs of Teldrassil are tainted by a growing darkness.\n\n[h3]Reputation[/h3]\n[npc=14725] has the Darnassus repeatable [quest=7800] used by non-night elven Alliance players to obtain the right to ride [url=?items=15.5&filter=na=Reins+-Winterspring;ra=4;cr=93:92;crs=2:1;crv=0:0]night sabers[/url].[pad]Players who are at or close to level 44 looking to gain the favor of Darnassus should find and complete the quests of [zone=357]. The quests therein are associated with Darnassus and could prove to substantially increase your reputation should they all be completed.'),(8,70,0,NULL,0,2,'The [b]Syndicate[/b] is a mostly Human criminal organization that operates primarily in the [zone=45] and the [zone=36], although a few small encampments are scattered in the [zone=267]. Their membership numbers around 3,000 persons.\n\nThey have three leaders: [npc=2423] (who took over from his father Aiden Perenolde), descendent of the original Lord of Alterac, who directs the Syndicate\'s actions in the Alterac Mountains from Strahnbrad; [npc=2597] directs Syndicate actions in Arathi Highlands from the main keep in the semi-abandoned fortress of Stromgarde; and Lady Beve Perenolde, daughter of Aiden Perenolde.\n\n[h3]History[/h3]\n\nDuring the Second War the Kingdom of Alterac, led by Lord Perenolde, was discovered to be in league with the Orcish Horde. Perenolde believed that a Horde victory was inevitable, and thus offered aid to the Horde by stirring up rebellions, attacking Alliance bases, and giving them supplies. When this treachery was discovered, the Alliance marched on Alterac and destroyed it. Perenolde and any nobles who went along with his plans were stripped of their titles and land. Many of the nobility managed to escape, however, and began plotting their revenge. Using their still sizable fortunes, the nobility hired a band of thieves and assassins, forming an organization known as the Syndicate.\n\nAt first the Syndicate\'s goal was just to spread chaos and disorder, striking from hidden bases in the Alterac Mountains. With the end of the Third War and the resultant chaos however, the leaders of the Syndicate saw their chance to return Alterac to its former power. They have now gained control of several outposts in the surrounding area including the sacked fortress of Durnholde Keep and a portion of the city of Stromgarde.\n\nThey are enemies of both the Alliance, whom they consider their mortal enemies, and the Horde, whom they consider mere brutes good for nothing but slave labor. As a result, the Syndicate is now hunted by both factions, with the [npc=10181], in particular, placing a bounty on their heads - guaranteeing that all captured Syndicate members will be summarily executed. In addition, [npc=4949] ordered a number of his agents, including [npc=2229], [npc=2239], [npc=2238] and their leader [npc=2316] to launch an investigation into the nature of the Syndicate and its activities, as well as to recover [item=3498], which belonged to a dear friend of his, [npc=18887] - a necklace now worn by Elysa, the mistress of Lord Aliden.\n\n[h3]Reputation[/h3]\n\nThe Syndicate as a faction in World of Warcraft is very odd in comparison to most factions in that the killing of the factions members will not lower your standing with the faction. For most players who are not a rogue, the only way for the Syndicate to appear on their Reputation Menu is to complete the quest [quest=8249], which is available to non-rogues. However, the quest requires [item=16885] ... which only rogues can obtain by pick-pocketing NPCs above level fifty, and those can only be traded to you - making it difficult to arrange such a transaction.\n\nCurrently there is only one known option to increase a player’s reputation with the Syndicate, and that is by killing members of the [faction=349] faction. There are no known rewards for increasing Syndicate reputation, and Ravenholdt-affiliated NPCs only give 1 Syndicate Reputation points, with the exception of [npc=13085], who gives 5 (although the corresponding loss of reputation with Ravenholdt is also five times as great). With all players starting at 32000/36000 hated with the faction, it would require killing 10,000 Ravenholdt NPCs to reach Neutral status with the faction; unfortunately, neutral status is the highest you can reach with the Syndicate, and if not to deter players further, none of the Ravenholdt NPCs drop loot.\n\n[b]WARNING[/b]: If you do decide to kill Ravenholdt NPCs, know that there is currently no way to restore your standings with Ravenholdt, if you do go below Neutral. The reason for the problem is that none of the quests that give Ravenholdt Reputation points will be available because none of the members from Ravenholdt will speak to you. This would mean its a permanent change and you will never be able to interact with any of the NPC loyal to Ravenholdt ever again. Also note that players start at 0/3000 reputation with Ravenholdt, and killing even one of their NPCs at this reputation level will forever prevent you from raising your reputation with them again.'),(8,72,0,NULL,0,2,'[b]Stormwind[/b] is the faction associated with [zone=1519], the capital of the humans. It is located in the northwestern part of [zone=12]. The child king, [npc=1747], resides in Stormwind Keep, surrounded by his body guards and advisors, [npc=1748] (the regent), and [npc=1749]. The city is named for the occasional sudden squalls created by a ley line pattern in the mountains around the glorious city.\n\n[h3]History[/h3]\nDuring the First War, the Kingdom of Azeroth, including its capital, Stormwind Keep, was utterly destroyed by the Horde and its survivors fled to Lordaeron. After the orcs were defeated at the Dark Portal at the end of the Second War, it was decided that the city would be rebuilt, even surpassing its former grandeur. The nobles of Stormwind assembled a team of the most skilled and ingenious stonemasons and architects they could find. Under their direction, Stormwind was rebuilt in an amazingly short period of time. Now, at the end of the Third War, in the renamed Kingdom of Stormwind, it stands as one of the last bastions of human power left in the world. \n\nWith the fall of the northern kingdoms, Stormwind is by far the most populated city in the world. Boasting a population of two-hundred thousand people (predominantly human), it serves in many ways as the cultural and trade center of the Alliance, even with remote access to the sea. The humans living in the city are generally carefree and artistic, favoring light and colorful clothes, cuisine and art. It is home to the Academy of Arcane Sciences, the only wizarding school in Eastern Kingdoms, as well as SI:7, a rogue intelligence organization.\n\nHowever, the people of Stormwind find it difficult to accept Theramore\'s role as the home of the new Alliance, convinced not only that Stormwind should be the legitimate heir of Lordaeron\'s role in the past, but also that Theramore is doing little against the worsening situation within the Eastern Kingdoms.\n\n[h3]Reputation[/h3]\n[npc=14722] has the repeatable cloth quests to achieve a higher reputation with Stormwind. In return for exalted reputation, non-human players are able to ride horses.\n\nMost quests associated with Stormwind come from the surrounding areas of Elwynn Forest, [zone=40], and [zone=44].'),(8,76,0,NULL,0,2,'[b]Orgrimmar[/b] is the faction for the capital city [zone=1637] of the orcs and trolls of the [faction=530]. Found at the northern edge of [zone=14], the imposing city is home to the orcish Warchief, [npc=4949].\n\n[h3]History[/h3]\nThrall led the orcs to the continent of Kalimdor, where they founded a new homeland with the help of their tauren brethren. Naming their new land Durotar after Thrall\'s murdered father, the orcs settled down to rebuild their once-glorious society. The demonic curse on their kind ended, the Horde changed from a warlike juggernaut into more of a loose coalition, dedicated to survival and prosperity rather than conquest. Aided by the noble tauren and the cunning trolls of the Darkspear tribe, Thrall and his orcs looked forward to a new era of peace in their own land. \n\nFrom there, they began the creation of the great warrior city, Orgrimmar. Named after the former Warchief, Orgrim Doomhammer, the new city was constructed in a short amount of time, with the aid of goblins, tauren, trolls, and the Mok\'Nathal Rexxar. Despite having some problems with the centaur, harpies, enraged thunder lizards, kobolds, evil orcish warlocks, quilboars, and unfortunately, the Alliance, Orgrimmar prospered in the end and became home to the orcs and Darkspear Trolls.\n\nToday, Orgrimmar lies at the base of a mountain between Durotar and [zone=16]. A warrior city indeed, it is home to countless amounts of orcs, trolls, tauren, and an increasing amount of Forsaken are now joining the city, as well as the Blood Elves who have recently been accepted into the Horde.\n\n[h3]Reputation[/h3]\n[npc=14726] has the Orgrimmar repeatable cloth quests used by non-orcish Horde players to obtain the right to ride [url=?items=15.5&filter=na=Wolf;cr=93:92;crs=2:1;crv=0:0]wolves[/url] at exalted.\n\nSurrounding areas Durotar and [zone=17] have the most quests for gaining reputation with Orgrimmar.'),(8,81,0,NULL,0,2,'[b]Thunder Bluff[/b] is the faction of the Tauren capital city [zone=1638] located in the northern part of the region of [zone=215]. The whole of the city is built on bluffs several hundred feet above the surrounding landscape, and is accessible by elevators on the southwestern and northeastern sides.\n\n[h3]History[/h3]\nThe great city of Thunder Bluff lies atop a series of mesas that overlook the verdant grasslands of Mulgore. The once nomadic Tauren recently built the city as a center for trade caravans, traveling craftsmen and artisans of every kind. It was established by the mighty chief [npc=3057] after the Tauren, with help from the orcs, drove away the centaurs that originally inhabited Mulgore. Long bridges of rope and wood span the chasms between the mesas, topped with tents, longhouses, colorfully painted totems, and spirit lodges. The Tauren chief watches over the bustling city, ensuring that the united Tauren tribes live in peace and security.\n\n[h3]Reputation[/h3]\n[npc=14728] has the Thunder Bluff repeatable cloth quests used by non-tauren Horde players to obtain the right to ride [url=?items=15.5&filter=na=Kodo;cr=93:92;crs=2:1;crv=0:0]kodos[/url] at exalted.\n\nSurrounding zones Mulgore and [zone=17] have the most quests for gaining reputation with Thunder Bluff.'),(8,87,0,NULL,0,2,'During the events leading up to and following the Third War, several criminal organizations appeared in Azeroth. The [b]Bloodsail Buccaneers[/b] appear to be one of these organizations, originating from the Bloodsail Hold on Plunder Isle and is where their ruler, Duke Falrevere holds court. They now plot to plunder and cripple the Steamwheedle Cartel controlled port town of [faction=21], currently under the protection of the Blackwater Raiders. It is likely the Bloodsail Buccaneers have come to take advantage of the town’s current loss of its fleet off the coast of the [zone=45], in which two of its ships were destroyed, and the remaining ship forced to find shelter in a cove, where its crew now fights to survive skirmishes with the Daggerspine Naga.\n\nIn preparation of the attack the Bloodsail Buccaneers have taken position in key locations near the town. Currently they have three ships anchored along the coastline south of Booty Bay, clear of the town’s defensive cannons, with camps also being built along the same coast in preparation of the attack. In addition, a scouting party has landed just west of the entrance to the town, reporting all activities, along with a compound being constructed along the road leading towards the town, likely to stop any re-enforcements from coming to help.\n\nBoth the Bloodsail Buccaneers and Blackwater Raiders seek to achieve their goals without having their forces engaged in battle, to this end each side now seek the aid of adventurers sympathetic to their cause.\n\n[h3]Reputation[/h3]\nThere is only one way to increase your reputation with the Bloodsail Buccaneers and that’s to unleash your wrath on any citizen of Booty Bay who can be found through out the Eastern Kingdoms. Below is a list of every citizen of Booty Bay and their reputation value. The amount gained with the Bloodsail Buccaneers is shown for a level 60 non-human. The amount lost for killing a citizen cannot be shown as it depends on your current level with Booty Bay and the importance of the person you kill. In addition to this what ever you lose with Booty Bay you will lose half of that in the other three goblin towns so if you lose 25 points in Booty Bay you will lose 12.5 points in [faction=470].\n\n[ul]\n[li][npc=4624]: 25 rep gained[/li]\n[li][npc=15088]: 25 rep gained[/li]\n[li][npc=2496]: 5 rep gained[/li]\n[li][npc=2636]: 5 rep gained[/li]\n[li][url=?npcs&filter=cr=3;crs=21;crv=0]Many more NPCs[/url]![/li]\n[/ul]\n\nThe fastest way to increase you reputation with the Bloodsail Buccaneers is to kill Booty Bay Bruisers. At first it may seem a simple task as the guards don\'t appear as threatening as the other monsters a player faces within the game. However, the guards are highly equipped to neutralize players of any class, to prevent people from attacking each other while in the town. What gives the Booty Bay Bruiser the advantage is several factors, one of them being their ability to use nets to lock you in place, preventing you from escaping. Another is the fact that they spawn every time you attack a citizen of the city or if you’re under Unfriendly status with Booty Bay the Bruisers can spawn if you enter a building, because of this players can soon find them selves swarmed by Bruisers.\n\nYet, theses are just the minor problems, in comparison to the Bruiser’s strongest ability, once it pulls out its gun its unlikely you will live, if you do not escape fast enough. Each time a guard shoots you, the attack throws you back, much like an Ogre hammer attack; the difference here is that the Bruiser can shoot in quick succession causing chain throw backs. A player can literally be thrown from one side of the town to the other, preventing you from attacking. More often you will find your self being forced into a corner, unable to move and unable to attack with each spell being interrupted by the Bruiser’s attack. Because the Bruisers do not put their guns away once they are out, the best course of action is to run away. \n\nThrough trial and error most people have discovered a safe place to kill Booty Bay Bruisers. If you follow the tunnel leading into the town, the path to your left that leads to the Blacksmith house is the ideal place to kill the guards. Only two guards patrol this path and normally don’t pass each other that closely, allowing both to be dispatched separately. Once they are gone, one can simply enter the first build on the path to cause a guard to spawn if they are below Unfriendly, if not they can simply attack one of the two NPC in the build, both of which are not high in level. Doing this a player should be able to kill 2 to 4 Bruisers before the two patrolling Bruisers re-spawn. On average a player doing this can kill about 30 to 40 Booty Bay Bruisers gaining about 800 reputation points with the pirates. The Bruisers here don’t appear to pull out their guns, but if you find your self in a bad situation, you can jump over the railing running along the path to the waters below, to escape.\n\n[h3]Rewards[/h3]\nBecoming friendly with the Bloodsail Buccaneers will grant you access to the following items:\n\n[ul]\n[li][item=12185] - Summons a [npc=11236][/li]\n[li][item=22742][/li]\n[li][item=22743][/li]\n[li][item=22745][/li]\n[/ul]\n\nYou will need Honored with the Bloodsail Buccaneers for [achievement=2336].'),(8,92,0,NULL,0,2,'[b]Gelkis[/b] are a tribe of centaur who have made their home in the southmost parts of [zone=405]. They are mortal enemies of the [faction=93], a brother tribe also located in southern Desolace. The founding leader, or Khan, of the Gelkis was [npc=13741], second of the alleged offspring of Zaetar and Theradras. They are presently lead by [npc=5602] and the clan representative [npc=5397]. \n\nThe Gelkis hold no alliance with their brother tribes, but have been known to act both hostile and passive towards members of the Alliance and Horde.\n\n[h3]History[/h3]\nOriginally lead by the Second Khan Gelk, the Magram situated themselves in the southernmost regions of Desolace when the centaur divided into five tribes and have remained there ever since. \n\nWhen the Gelkis tribe spoke out against Khan Magra of the Magram\'s notion that strength was essential and the tribe’s survival depended on their fighting spirit, arguing that Theradras always watches over the centaur and will keep the tribes safe and alive, an eternal feud between the two tribes was born. \n\nAs such the Gelkis are more civilized - or as close as centaur can come to civilized - than their brethren, with an organised social structure and a firm grasp of the Common tongue. While the Magram only respect strength, the Gelkis respect nature and their birthmother Theradras, calling upon her protection and the power of earth to maintain their existence. Though the Magram view this as weak it would seem to be an erroneous view, as Earth Elementals can be sighted in Gelkis Village, putting an end to unwelcome intruders alongside their centaur masters.\n\n[h3]Reputation[/h3]\nOne of the two factions situated in Desolace, you are required to have a certain amount of reputation with the Gelkis in order to start their quests. Reputation for the Gelkis can be gained by killing [url=?npcs=7&filter=na=Magram]Magram monsters[/url]. When killing Magram monsters, you gain 20 reputation with Gelkis and lose 100 with the Magram tribe.'),(8,93,0,NULL,0,2,'[b]Magram[/b] are a tribe of centaur who have made their home in the southeastern parts of [zone=405]. They are mortal enemies of the [faction=92], a brother tribe also located in southern Desolace. The founding leader, or Khan, of the Magram was [npc=13740], third of the alleged offspring of Zaetar and Theradras. They are presently lead by [npc=5601] and the clan representative [npc=5398]. \n\nThe Magram hold no alliance with their brother tribes, but have been known to act both hostile and passive towards members of the Alliance and Horde.\n\n[h3]History[/h3]\nOriginally lead by the Third Khan Magra, the Magram situated themselves against the mountain ranges of Desolace when the centaur divided into five tribes and have remained there ever since. \n\nBefore the death of Magra, he installed the idea that strength was essential and the tribe’s survival depended on their fighting spirit. When their brother tribe of Gelkis centaur spoke out against this notion, arguing that Theradras always watches over the centaur and will keep the tribes safe and alive, an eternal feud between the two tribes was born. \n\nThe life-long pursuit of strength has carried on through the Khans of Magram to this day, turning them violent and determined. To solidify their title as the strongest the tribe still fights fiercely to weaken or destroy their brother clans, viewing the Kolkar as weak, the Gelkis as nothing more than a nuisance, and the Maraudine as a formidable enemy. \n\nIt can be assumed that the Magram’s culture has developed into revolving around strength worship above all else. When compared to the Gelkis, the Magram hold very primitive forms of speech and social structure. For example, their grasp of common is limited and the position of Khan would likely be sought through a death match of sorts.\n\n[h3]Reputation[/h3]\nOne of the two factions situated in Desolace, you are required to have a certain amount of reputation with the Magram in order to start their quests. Reputation for the Magram can be gained by killing [url=?npcs=7&filter=na=Gelkis]Gelkis monsters[/url]. When killing Gelkis monsters, you gain 20 reputation with Magram and lose 100 with the Gelkis tribe.'),(8,270,0,NULL,0,2,'[b]Zandalar Tribe[/b] trolls have come to Yojamba Isle in [zone=33] in the effort to recruit help against the resurrected Blood God and his Atal\'ai Priests in [zone=19] and in the [zone=1417].\n\n[h3]History[/h3]\nThe Zandalarians were the earliest known trolls, the first tribe from which all tribes originated. Over time two distinct troll empires emerged - the Amani and the Gurubashi. They existed for thousands of years until the coming of the Night Elves, who warred with them and eventually drove both empires into exile. \n\nFollowing the Great Sundering, the defeated Gurubashi grew ever more desperate to eke out a living. Searching for a means to survive, they enlisted the aid of the savage [npc=14834], also known as the Soulflayer. Hakkar grew into a merciless oppressor who demanded daily sacrifices from his devotees, and so in time the Gurubashi turned on their dark master. The strongest tribes (including the Zandalar) banded together to defeat Hakkar and his loyal troll priests, the Atal\'ai. The united tribes narrowly defeated the Blood God and cast out the Atal\'ai... despite their victory, however, the Gurubashi Empire soon fell. \n\nIn recent years the exiled Atal\'ai priests have discovered that Hakkar\'s physical form can only be summoned within the ancient and once-deserted capital of the Gurubashi Empire, Zul\'Gurub. Unfortunately, the priests have met with success in their quest to call forth Hakkar—reports confirm the presence of the dreaded Soulflayer in the heart of the ruins. \n\nAnd so the Zandalar tribe has arrived on the shores of Azeroth to battle Hakkar once again. But the Blood God has grown increasingly powerful, bending several tribes to his will and even commanding the avatars of the Primal Gods— Bat, Panther, Tiger, Spider and Snake. With the tribes splintered, the Zandalarians have been forced to recruit champions from Azeroth\'s varied and disparate races to battle, and hopefully once again defeat, the Soulflayer.\n\n[h3]Reputation[/h3]\nReputation with the Zandalar Tribe is gained from killing trash and bosses in Zul\'Gurub as well as repeatable and special quests which require instance-dropped items to complete. Each full run of Zul\'Gurub gives approximately 2,500-3,000 reputation.\n\nBefore the Burning Crusade, the main reason for gaining reputation with the tribe were the [url=?items=0.6&filter=na=Zandalar]shoulder[/url], [url=?items=0.6&filter=minrl=60;maxrl=60;cr=18:107;crs=4:0;crv=0:to+a+leg+or+head+slot+item]head and leg[/url] slot item enchants. As well, there were popular alchemy and enchanting recipes that many end-game guilds sought after. All rewarded items from the item set within Zul\'Gurub required a set level of reputation.'),(8,349,0,NULL,0,2,'[b]Ravenholdt[/b] is a guild of thieves and assassins that welcomes only those of extraordinary prowess into its fold. They are diametrically opposed to the [faction=70], and are a rogue-only faction as all quests are rogue-only quests. The exception is the quest [quest=8249], which is available to non-rogues, but they would require the help of a rogue to get the items for the quest. [b]Ravenholdt Manor[/b], the faction\'s headquarters, is located in [zone=36], but to get there you have to come from the northeast corner of [zone=267].\n\n[h3]Reputation[/h3]\nAll Syndicate [url=?search=Syndicate#npcs]humanoids[/url] give 1-5 reputation points per kill depending on your current level. As well, there are a few quests that increase your reputation, but your primary method to raise your reputation is from the repeatable quests for turning in pickpocketed items.\n\nYou start off at 0/3000 Neutral with Ravenholdt, meaning if you kill any Ravenholdt NPCs before raising your reputation by at least 5, you will become Unfriendly and be unable to raise your reputation any higher ever again. To raise your reputation from Neutral to Friendly, the repeatable quest [quest=6701] is available. You will have to turn in 11-12 [item=17124] and once you are Friendly, this quest is no longer an option. From Neutral to Friendly you can also deliver five [item=16885] for Junkboxes Needed.\n\nTo raise your reputation beyond Friendly, the only choice is the repeatable quest Junkboxes Needed. There is no known faction reward for obtaining Friendly, Honored, Revered or Exalted, except that the guards speak to you with more respect. However, Exalted is required to obtain the Feat of Strength [achievement=2336].'),(8,369,0,NULL,0,2,'[minibox]\n[h2]Steamwheedle Cartel[/h2]\n[faction=21]\n[faction=577]\n[b]Gadgetzan[/b]\n[faction=470]\n[/minibox]\n\n[b]Gadgetzan[/b] is the faction of the city Gadgetzan, which is home to goblinhood\'s finest engineers, alchemists and merchants and is the only spot of civilization in the entire desert. Rising out of the northern [zone=440] desert like an oasis, Gadgetzan is the headquarters of the Steamwheedle Cartel, the largest of the Goblin Cartels. The Goblins believe in profit above loyalty, thus Gadgetzan is considered neutral territory in the Horde/Alliance conflict.\n\n[h3]History[/h3]\nAlthough the goblins\' neutrality is almost universally acknowledged, there are still those who seek to sow chaos and anarchy. For Gadgetzan, this comes in the form of the Wastewander bandits, a gang of miscreants who have occupied the Waterspring Field and Noonshade Ruins of northeast Tanaris. Few goblins care about ancient ruins (unless they have treasure) – for all they care, the bandits can have the old blocks of stone. \n\nHowever, the Waterspring Field is vital to the goblins\' survival in the desert, providing them with the liquid gold of the desert. Water towers out in the field were constructed under the blazing heat of the desert sun by the backbreaking work of their slaves, and by Az, the goblins aren\'t going to give up their hard earned towers that easily. However, the Bruisers need to stay in town to keep the gnomes\' collective Napoleonic-complex from getting out of hand and to stop the seemingly endless dueling among the various visitors from disrupting business. Therefore, it falls to brave mercenaries from all corners of the world to help the goblins in their time of utmost need.\n\n[h3]Reputation[/h3]\nKilling the [url=?npcs=7&filter=na=Southsea]Southsea[/url] and [url=?npcs=7&filter=na=Wastewander]Wastewander[/url] monsters will increase your reputation with the Steamwheedle Cartel. Having a friendly or higher reputation will make the guards help you in case of initiated violence against you. Having an exalted reputation means that the guards will never attack you even if you initiate attacks on the opposite faction.\n\nMost of the quests associated with the Gadgetzan faction are located in Tanaris.\n\nIf you are Hated with Gadgetzan, you can do the repeatable quest [quest=9268] to obtain Neutral.'),(8,470,0,NULL,0,2,'[minibox]\n[h2]Steamwheedle Cartel[/h2]\n[faction=21]\n[faction=577]\n[faction=369]\n[b]Ratchet[/b]\n[/minibox]\n\n[b]Ratchet[/b], the faction of the city Rachet on Kalimdor’s central east coast in [zone=17], is run by goblins and shows it. Its streets sprawl in every direction, and the architecture shows no consistency or common vision. It is a city of entertainment and trade, where anything that anyone would ever want to buy — and plenty of things that no one ever wants to buy — is on sale.\n\nRatchet is currently run by a corporate group known as the Steamwheedle Cartel a splinter group from the Venture Company, who first built the port town for trading with [zone=1637]. It is initially a neutral faction to both Horde and Alliance. A ferry conveniently connects Ratchet to Booty Bay.\n\n[h3]History[/h3]\nBuilt from equal parts of industry and decadence, the goblin port city of Ratchet sprawls along nearly a mile of of coastline where the eastern Barrens poke between [zone=14] and the [zone=15] to the sea. Ratchet is the pride of the goblins, a trade city where you can find almost anything your heart desires - and if something is not in stock, you can bet the goblins can order it. Ratchet also had regular ferries that traversed the safe though roundabout route to the island stronghold of Theramore to the south.\n\nRatchet is a city where creatures who were once the butt of jokes now reign supreme. Its streets wander without rhyme or reason through neighborhoods dedicated to one activity: commerce. Ramshackle warehouses stand next to stately stone homes. Fine shops press cheek to jowl with rude huts. Wares of every type imaginable - and some beyond the imagination - are on display in markets and in exclusive boutiques.\n\nGoblins welcome anyone with gold or items of value and a willingness to trade them for their wares and services. Merchants throng the marketplaces each day, selling everything from silks to slaves, and even at night the stores lining the twisting streets and alleys remain open for business. Those with the money can listen to skilled musicians while drinking fine ales and eating food prepared by expert chefs. For those with earthier tastes, the streets along the wharf teem with whorehouses, taprooms, and casinos.\n\nRatchet is the largest port on Kalimdor, with as many ships bringing cargo in as there are ships heading out for other sites around Kalimdor. In addition to legitimate trade vessels, pirate craft receive amnesty while in the port of Ratchet as long as they can pay the stiff docking fees. This situation makes many merchant captains furious, but they cannot hope to stay in business if they boycott Ratchet. Moreover, the Lawkeepers and hired mercenaries prowling the waterfront are eager to deal with anyone looking to cause trouble.\n\n[h3]Reputation[/h3]\nMost of the quests to raise reputation with Ratchet and the Steamwheedle Cartel are located in the Barrens. Having a friendly or higher reputation will make the guards help you in case of initiated violence against you.\n\nIf you are Hated with Rachet, you can do the repeatable quest [quest=9267] to get back to Neutral.'),(8,471,0,NULL,0,2,'The Wildhammers are a clan of dwarves currently centered in the [zone=47] and [zone=3520]. The faction has been removed in patch 2.0.1.\n\n[h3]History[/h3]\n\nJust prior to the [object=175739], the Wildhammer Clan, ruled by Thane Khardros Wildhammer, inhabited the foothills and crags around the base of Ironforge. The Wildhammer Clan was unsuccessful in wresting control of [zone=1537] from the Bronzebeard and Dark Iron clans. Khardros and his Wildhammer warriors traveled north through the barrier gates of Dun Algaz, and founded their own kingdom within the distant peak of Grim Batol. There, the Wildhammers thrived and rebuilt their stores of treasure.\n\n[npc=9019] and his Dark Irons vowed revenge against Ironforge. Thaurissan and his sorceress wife, Modgud, launched a two-pronged assault against both Ironforge and Grim Batol. As Modgud confronted the enemy warriors, she used her powers to strike fear into their hearts. Shadows moved at her command, and dark things crawled up from the depths of the earth to stalk the Wildhammers in their own halls. Eventually Modgud broke through the gates and laid siege to the fortress itself. The Wildhammers fought desperately, Khardros himself wading through the roiling masses to slay the sorceress queen. With their queen lost, the Dark Irons fled before the fury of the Wildhammers.\n\nOnce the immediate Dark Iron threat was eliminated, the Wildhammers returned home to Grim Batol. However, the death of the Modgud had left an evil stain on the mountain fortress, and the Wildhammers found it uninhabitable. Khardros took his people north towards the lands of Lordaeron. Settling within the mountainous region of the Aerie Peaks and The Hinterlands, and lush forests of Northeron, the Wildhammers crafted the city of Aerie Peak, where the Wildhammers grew closer to nature and even bonded with the mighty gryphons of the area. Over time they started calling their land the Hinterlands. \n\n[b]Modern Wildhammers[/b]\nThe Wildhammer Clan currently makes its home at Aerie Peak in the Hinterlands. The most immediate threat to their security comes from the east in the form of the Witherbark Trolls and Vilebranch Trolls. They are most famous for riding into battle atop Gryphons, while wielding powerful Stormhammers.\nWildhammer dwarves have a number of clans, each ruled by a Thane. The strongest Thane rules Aerie Peak.'),(8,509,0,NULL,0,2,'[b]The League of Arathor[/b] was originally established by the survivors of the Kingdom of Stromgarde to reclaim the [zone=45] from the hands of the Forsaken Defilers in Hammerfall. Today it is an organization in support of the Alliance, based out of the [zone=3358] in Refuge Pointe. They have taken it upon themselves to help supply the Alliance forces where needed, and their members include all manner of Alliance races - even though they are still predominantly Stromgardian humans.\n\n[h3]Reputation[/h3]\nPlayers can earn reputation in this faction by participating in the Arathi Basin battleground. When you fight in Arathi Basin you earn 10 reputation per 160 resources. On Arathi Basin holiday weekends the required resources is reduced to 150.\n\nYou are granted the player title [title=48] once exalted with League of Arathor and the other two battleground factions, [faction=890] and [faction=730].'),(8,510,0,NULL,0,2,'[b]The Defilers[/b] seek to foil the [faction=509] in the [zone=3358] battleground. Today it is an organization in support of the Horde, based out of Hammerfall in [zone=45]. They have taken it upon themselves to help supply the Horde forces where needed, and their members include all manner of Horde races - even though they are still predominantly orcs.\n\n[h3]Reputation[/h3]\nReputation is gained through participation in the Arathi Basin battleground. When you fight in Arathi Basin you earn 10 reputation per 160 resources. On Arathi Basin holiday weekends the required resources is reduced to 150.\n\nYou are granted the player title [title=47] once exalted with the Defilers and the other two battleground factions, [faction=889] and [faction=729].'),(8,529,0,NULL,0,2,'The [b]Argent Dawn[/b] is an organization focused on protecting Azeroth from the threats that seek to destroy it, such as the Burning Legion and the Scourge. Strongholds of the Argent Dawn can be found in the [zone=139] and [zone=28]. It also maintains a presence in [zone=1657] and in the [zone=85], among other less notable areas. Reputation with the Argent Dawn can be used to purchase various profession recipes, misc. consumables, and to mitigate the cost of attunement to [zone=3456]. With the expansion of the Burning Crusade, Argent Dawn reputation has decreased in value.\n\nArgent is Latin for silver, which could explain why the [item=22999] has an icon of a silver sun rising.[h3]History[/h3]After the death of the [npc=16062], the corruption of the Scarlet Crusade became apparent to some of its members, who subsequently left the ranks of the [url=?search=scarlet+crusade#M0z]Scarlet Crusade[/url] and established the Argent Dawn to protect Azeroth from the threat of the Scourge without the blind zealotry present in the Scarlet Crusade.\n\nWhile they share the same goals as the Crusade, the Argent Dawn has opened its ranks to not only other Alliance races besides Humans, but also members of the Horde and even some of the Forsaken. They caution discretion and introspection, and put a lot of emphasis on researching the Scourge and how to combat them.\n\nWith time the Argent Dawn has grown diversified, and like its progenitor — the Scourge — has split again, with an offshoot called the [url=?search=brotherhood+of+the+light]Brotherhood of the Light[/url], a compromise between the Argent Dawn\'s more scholarly approach and the Scarlet Crusade\'s fanaticism.\n\n[h3]Reputation[/h3]\n[b]Scourgestones[/b]\nWhile wearing a trinket granting the Argent Dawn Commission effect, characters can loot [url=?items=12&filter=na=scourgestone]scourgestones[/url] from undead monsters they\'ve killed, and subsequently turn them in in exchange for [item=12844]. These turn-ins require various numbers of [item=12843], [item=12841], and [item=12840]. It should be noted that the token items received from the turn-ins should be saved until after Revered status is reached, as the quest turn-ins will no longer grant reputation after this point.[pad][b]Cauldrons[/b]\nAnother way to gain reputation with the Argent Dawn is through repeatable \"Cauldron\" quests. The Cauldrons are a source of \"undeathness,\" that contribute to the Scourge\'s numbers.[pad][b]Instances[/b]\nLike most factions, the player can run instances to increase his reputation. These instances are [zone=2017] and [zone=2057]. Naturally, these instances also include quests that will raise Argent Dawn reputation, as well as include Scourgestone drops.'),(8,530,0,NULL,0,2,'[b]Darkspear Trolls[/b], the tribe of exiled trolls that has joined forces with [npc=4949] and the Horde. They now call [zone=1637] their home, which they share with their orc allies. [npc=10540] is their current leader.\n\n[h3]History[/h3]\nAs tribal rivalries erupted throughout the former Gurubashi Empire, the Darkspear Tribe found themselves driven from their homeland in [zone=33]. Having settled in what are believed today to be the Broken Isles, the tribe soon found themselves entangled in a conflict with a band of murlocs. Their fate seemed sealed until the orcish Warchief Thrall and his band of newly freed orcs took shelter on their island home. Controlled by a Sea Witch, a group of rampaging murlocs captured the Darkspears\' leader Sen\'jin, along with Thrall and several other orcs and trolls. Thrall managed to free himself and others, but was ultimately unable to save the trolls\' leader. Although Sen\'jin was sacrificed to the Sea Witch, he was able to reveal a vision he had in which Thrall would lead the Darkspear from the island. \n\nAfter returning to the island, Thrall and his followers managed to fend off further attacks by the Sea Witch and her murloc minions, and set sail for Kalimdor once again. Under the new leadership of [npc=10540], the Darkspear swore allegiance to Thrall\'s Horde and followed him to Kalimdor. Now considered enemies by all other trolls except the Revantusk and the Zandalari, the Darkspear are held in contempt to this day. Yet, the Darkspear have not forgotten being driven from their ancestral homes and this animosity is eagerly returned, especially towards the other jungle trolls. Having reached the orc\'s new homeland, [zone=14], the trolls carved out another home for themselves - this time among the Echo Isles on the eastern shores of the new orc kingdom. \n\nHowever, with the coming of Kul Tiras and its navy, the Darkspear were forced to retreat inland under the onslaught of the misguided commander [npc=177201]. The trolls, fighting alongside their horde brethren, defeated the enemy and reclaimed their new homeland. Shortly thereafter, a witch doctor by the name of [npc=3205] began using dark magic to take the minds of his fellow Darkspear. As his army of mindless followers grew, Vol\'jin ordered the free trolls to evacuate, and Zalazane took control of the Echo Isles. The Darkspear have since settled on the nearby shore, naming their new village after their old leader, Sen\'jin. From Sen\'jin Village they, along with their allies, send forces to battle Zalazane and his enslaved army.\n\n[h3]Reputation[/h3]\n[npc=14727] has the repeatable cloth reputation quests. As a reward for being exalted with the Darkspear Trolls, non-troll Horde players are able to ride [url=?items=15.5&filter=na=Raptor;cr=93:92;crs=2:1;crv=0:0]raptors[/url].\n\nSurrounding zone Durotar contain the most quests for gaining reputation with the Darkspear Trolls. As well, higher level players with the Burning Crusade also have a good amount of quests in [zone=3521].'),(8,576,0,NULL,0,2,'As the last uncorrupted furbolg tribe (at least in their view), the [b]Timbermaw[/b] seek to preserve their spiritual ways and end the suffering of their brethren.\n\nThe Timbermaw Furbolgs inhabit two areas: [zone=16] and [zone=361]. They are presumed to be the only furbolg tribe to escape demonic corruption, though this may not be true due to the existence of [npc=3897], an uncorrupted furbolg of unknown tribe, and the Stillpine tribe on [zone=3524] in Burning Crusade. However, many other races kill furbolg blindly now, without bothering to see if they are friend or foe. For this reason, the Timbermaw furbolg trust very few.\n\nAdventurers who seek out Timbermaw Hold in northern Felwood and prove themselves as friends of the Timbermaw will learn that the furbolgs value their friends above all else. Though they possess no fine jewels or any worldly riches, the Timbermaw\'s shamanistic tradition is still strong. They know much about the art of crafting armors from animal hides, and they are more than happy to share their healing/resurrection knowledge with friends of their tribe. Besides, any reputation above Unfriendly will also grant you untroubled access to [zone=493] and [zone=618] through their tunnels.\n\n[h3]Reputation[/h3]\nReputation with the Timbermaw Hold faction is mainly gained through quests and killing in Felwood. The members of the Deadwood Tribe, another Furbolg tribe in Felwood, are the Timbermaws\' main enemies.\n\n[ul]\n[li]Killing one [url=?npcs&filter=na=Winterfall]Winterfall[/url] or [url=?npcs&filter=na=Deadwood]Deadwood[/url] Furbolg gives 10 reputation points. Gains stop at revered; Deadwoods give 2 reputation point at honored.[/li]\n[li]Killing either one of the Deadwood Bosses [npc=9464] or [npc=9462], is worth 60 reputation. There is no reputation limit.[/li]\n[li]Killing the elite Winterfall Furbolg, [npc=10738], located in a cave east of [faction=577], awards 50 reputation. There is no reputation limit, and his respawn rate is 6 to 8 minutes.[/li]\n[li]Killing the named rare mob [npc=14342] is worth 50 reputation. He is a rare spawn at Deadwood Village in Felwood and there is no reputation limit for this mob.[/li]\n[li]Killing the named rare mob [npc=10199] is worth 50 reputation. He is a rare spawn at Winterfall Village in Winterspring. Killing him will grant reputation up until Revered.[/li]\n[li]After completing [quest=8460], turning in 5 [item=21377] yields 150 reputation.[/li]\n[li]After completing [quest=8464], you will be able to turn in [item=21383] collected from furbolgs in Winterspring. Turning in 5 beads at [npc=11556] yields 150 reputation.[/li]\n[/ul]'),(NULL,NULL,0,'help=commenting-and-you',0,2,'[menu tab=2 path=2,13,0]One of many useful features is the user-submitted comment system. This system allows users to submit their own comments to augment the data provided here. As a rule, we promote the submission of informative comments, but we also like to see the occasional joke. Moderators and users alike will apply positive and negative ratings to comments in an effort to promote the useful ones and purge unnecessary information.\r\n\r\nWith that in mind, below is a guide that can be used to determine how your comment will likely be received by the community. \r\n\r\n[pad]\r\n\r\n[tabs name=comments]\r\n\r\n[tab name=\"Before you post\"]\r\n\r\n[ul]\r\n[li][b]Read existing comments[/b] – Sometimes, the information you have may already have been posted by another user. In this case, if the information is useful, the existing comment should be given a positive rank. Posting information that was already added in a previous comment will likely result in a negative rating.[pad][/li]\r\n[li][b]Verify your facts[/b] – Make sure that what you have to post is true. A friend might tell you that a mob is immune to Frost Nova, but unless you verify that yourself, you could be posting a potentially misleading comment.[pad][/li]\r\n[li][b]Temporary usability[/b] – If you want to correct invalid or missing information on a page, keep in mind that your comment may go from a positive ranking to a negative ranking when the correction occurs. For example, informing the community that a spell is cast by Illidan Stormrage before that data has been collected will be useful at first, but once Aowow learns to parse that information and adds it to the \'Abilities\' tab, your comment becomes redundant. If you do not want to worry about the comment or do not want one of your comments to be rated negatively, consider informing us in the [url=/?forums&board=1.]Site Feedback[/url] forum. The moderation staff will be happy to add a comment to correct invalid or missing information on the page for you. Alternatively, you can delete your comment later when it becomes redundant.[/li]\r\n[/ul]\r\n\r\n[/tab]\r\n\r\n[tab name=\"Comment ratings\"]\r\n\r\n[h3][color=q2]Positive (+1)[/color][/h3]\r\n[ul]\r\n[li][b]Corrections on drop percentages[/b] – There are many instances where drop percentages will be inaccurate. For example, quest items do not drop for people who do not have the quest, so their drop percentages will be low. Also, mobs that periodically do not drop loot when they die won\'t count against the drop percentages, so these mobs may appear to have higher drop rates for some items.[pad][/li]\r\n[li][b]Strategies[/b] – If you have a strategy that can assist other users in completing a quest or defeating a mob, by all means, share![pad][/li]\r\n[li][b]Quest coordinates[/b] – Providing coordinates for the location of quest items or mobs is always useful. When possible, you should provide links to quest targets as well.[pad][/li]\r\n[li][b]Theorycrafting[/b] – We encourage users to post any information they have regarding complex calculations they may have performed to, for example, prove one item has a higher DPS than another given certain abilities.[pad][/li]\r\n[li][b]Just for laughs[/b] – If your comment is one that would be universally funny (i.e. not an inside joke), post away. We like to laugh as much as anyone else. Of course, whether your joke is funny or not is subject to our other users. :)[/li]\r\n[/ul]\r\n\r\n[h3][color=q10]Negative (-1)[/color][/h3]\r\n[ul]\r\n[li][b]Redundant information[/b] – For instance, a comment that says \"Dropped by Ragnaros\" does not add anything to the page as that information can be viewed in the \"Dropped By\" tab of the page in question.[pad][/li]\r\n[li][b]Soloed by:[/b] Unless your comment contains a detailed explanation of how you defeated a mob, these comments do not add anything to the page. Simply stating your level, class, and that you soloed the mob by using a few skills is not enough to be useful.[pad][/li]\r\n[li][b]Dropped in X kills[/b] – Telling users that you were lucky enough to get the crusader enchant in one drop is not considered useful information.[pad][/li]\r\n[li][b]NPC/Object coordinates[/b] – The coordinates for NPC or mobs are already supplied in convenient maps within the interface.[pad][/li]\r\n[li][b]Best X before level Y[/b] – Simply posting that an item is the best twink weapon or the best dagger for a rogue is not helpful unless you can back up that claim with facts.[pad][/li]\r\n[li][b]HUNTAR WEPPON[/b] – While it would be acceptable to explain why you feel a certain class with a certain spec would gain the most benefit from an item, simply stating that you feel the weapon should always go to a hunter in a raid will result in negative moderation.[pad][/li]\r\n[li][b]Confirmed![/b] – Adding a comment that simply indicates that you have confirmed a comment left by someone else clutters the comments. The best way to confirm a comment as correct is to give it a positive ranking. A comment with a high ranking will indicate to users that many people think it is useful data.[/li]\r\n[/ul]\r\n\r\n[/tab]\r\n\r\n[tab name=Deletion]\r\n\r\nAny comment that does not abide by the same [forumrules] will be deleted by a moderator.\r\n\r\n[/tab]\r\n\r\n[/tabs]'),(NULL,NULL,0,'help=item-comparison',0,2,'[menu tab=2 path=2,13,5]Can\'t find the answer you were looking for? Just [url=/?aboutus#contact]contact us[/url], or post on our [url=/?forums&board=1]forums[/url]! \r\n\r\n[pad]\r\n\r\n[tabs name=compare]\r\n\r\n[tab name=\"General usage\"]\r\n\r\n[h3]Basic Controls[/h3]\r\n\r\n[ul]\r\n[li][img src=STATIC_URL/images/icons/save.gif border=0] [b]Save[/b] – Saves the comparison so that you may continue browsing the site without losing it. When you click on the [b]Compare[/b] button found throughout the site you will be given the option to add to your saved comparison.[/li]\r\n[li][img src=STATIC_URL/images/icons/refresh.gif border=0] [b]Autosaving[/b] – Indicates that you are viewing your saved comparison, and that any changes you make will automatically be saved. To avoid modifying your saved comparison, you may click on Link to this comparison before making any changes.[/li]\r\n[li][img src=STATIC_URL/images/icons/link.gif border=0] [b]Link to this comparison[/b] – Provides a link to a new page with the current item comparison already there! Useful for showing friends your item comparisons.[/li]\r\n[li][img src=STATIC_URL/images/icons/delete.gif border=0] [b]Clear[/b] – Removes all items, groups, and weights from the comparison tool, giving you a clean slate to work with. [b]This will [u]delete[/u] your saved comparison if used while autosaving.[/b][/li]\r\n[li][img src=STATIC_URL/images/icons/add.gif border=0] [b]Weight scale[/b] – Allows you to add one or more weight scales to the item comparison using your own weights or one of our predefined presets. Each weight scale can have its own name. A saved comparison also contains the weight information, allowing you to store custom weight scales for future use.[/li]\r\n[li][img src=STATIC_URL/images/icons/add.gif border=0] [b]Item[/b] – Opens a live search that displays item suggestions as you type the name of an item. Clicking on a suggestion will add that item to your comparison.[/li]\r\n[li][img src=STATIC_URL/images/icons/add.gif border=0] [b]Item set[/b] – Opens a live search that displays item set suggestions as you type the name of an item set. Clicking on a suggestion will add all of the items in that set to your comparison.[/li]\r\n[/ul]\r\n\r\n[h3]Adding Items[/h3]\r\n[div float=right align=right][img src=STATIC_URL/images/help/item-comparison/addingitems.gif]\r\n[small]Some of the ways to add items to a comparison.[/small][/div]The comparison tool is fully integrated with our site and designed to be as convenient as possible to work with. There are many ways to add items to a comparison depending on what part of the site you are on: \r\n[ul][li]Using the [url=/?compare]item comparison tool[/url] itself, you may add items or item sets using the links in the top right corner as described above.[/li]\r\n[li]Viewing an [url=/?item=35137]item[/url] or [url=/?itemset=-17]item set[/url] page, you may click on the red [b]Compare[/b] button near the Quick Facts box.[/li]\r\n[li]Viewing [url=/?items=4.2&filter=sl=8]search results[/url] or [url=/?npc=34077#sells]any page with a list of items[/url], checkboxes are displayed next to items which can be equipped. You may select one or more items and click the [b]Compare[/b] button at the top of the list.[/li][/ul]\r\n\r\n[i]Note: If you have a comparison saved, and you add items to your comparison from elsewhere on the site, you will be given the option to add them to your saved comparison or create a new one. If you don\'t have a saved comparison, a new comparison will automatically be created and saved with the selected items.[/i]\r\n\r\n[h3]Managing Your Items[/h3]\r\n[div float=right align=right][img src=STATIC_URL/images/help/item-comparison/newgroup.gif]\r\n[small]Creating a new group by dragging an item.[/small][/div]\r\n[ul][li][b]Creating a new group[/b] – [u]Drag an item into the empty column[/u] on the right to create a new group containing that item.[/li]\r\n[li][b]Moving[/b] – To move an item or group, click on the item (or the group\'s control bar) and [u]drag it to the desired position[/u].[/li]\r\n[li][b]Copying[/b] – [u]Holding shift while dragging[/u] an item or group will make a copy of it when it is dropped.[/li]\r\n[li][b]Deleting[/b] – Items and groups can be deleted by [u]dragging them out of the row[/u]. Groups may also be deleted by clicking the X on the right side of the group\'s control bar.[/li]\r\n[li][b]Deleting all but one group[/b] – [u]Holding shift while deleting a group[/u] (see above) will cause all other groups to be deleted instead of that one.[/li]\r\n[li][b]Splitting a group[/b] – Groups of 2 or more items can be split by [u]clicking on [b]Split[/b] in the menu dropdown[/u] on the group\'s control bar. This will create a new group for each item in the current group.[/li]\r\n[li][b]Exporting a group[/b] – [u]Clicking on [b]Export[/b] in the menu dropdown[/u] of the group\'s control bar will take you to a new comparison containing only the current group.[/li]\r\n[li][b]Item Enhancements[/b] - To add gems or enchantments to an item, [u]right-click on the item icon at the top[/u], then select the desired option from the menu. The stats will automatically update—including the set bonuses.[/li]\r\n[/ul]\r\n\r\n[/tab]\r\n\r\n[tab name=\"Advanced features\"]\r\n\r\n[h3]Level Adjustments[/h3]\r\nYou can select your desired character level from the dropdown at the top left. When you do, all the statistics that change according to your level (including combat ratings and heirloom item stats) will automatically adjust to the corresponding value for the level you\'ve entered.\r\n\r\n[h3]Gains[/h3]\r\nAt the bottom of the item comparison is a special row called \'Gains\'. The gains row calculates the minimum values of all stats that appear in any group in the item comparison. It then displays the bonuses each row has [b]above[/b] this minimum.\r\n\r\nFor example, the minimum stamina for any group in [url=/?compare=35031;35030;35029;35028;35027]this comparison[/url] is 50. The gains row displays nothing for the items which have 50 stamina, +23 sta for the item with 73 stamina, and +27 sta for the items with 77 stamina.\r\n\r\nBasically, the gains row removes the shared stats between all groups so that you can focus on what each group brings to the table.\r\n\r\n[h3]Focus Group[/h3]\r\n\r\n[screenshot url=STATIC_URL/images/help/item-comparison/focus2.gif thumb=STATIC_URL/images/help/item-comparison/focus.gif float=right]Comparing arena sets of the first four PvP\r\nseasons using a focus group.[/screenshot]Setting a focus group is done by clicking on the eye icon in the group\'s control bar. Selecting a group as your focus will update the display of the item comparison to show the difference in stats between all other groups and the focus group.\r\n\r\nWhen a focus is set, the focus group is highlighted and each other group has numbers that indicate the stats gained or lost in comparison to the focus group.\r\n\r\n[b][color=q2]Positive[/color][/b] numbers indicate that group has a higher total for a given stat than the focus group, while [b][color=q10]negative[/color][/b] numbers indicate that group has a lower total for a given stat than the focus group. \r\n\r\n[h3]Stat Weighting[/h3]\r\nTo add a weight scale to your comparison, click on the [b]Add a weight scale[/b] link in the top right corner. You may select a weight scale from our predefined presets or create one of your own. Each weight scale may be given a name that will appear in the score tooltips to help differentiate the different scores. You may add as many weight scales as you like.\r\n\r\nTo remove a weight scale, click on the [b]X[/b] next to the appropriate score in any group. To toggle between normalized (default), raw, and percent score mode, click on the score in any group.\r\n\r\nUnlike the weighted item search, these weight scales [b]do not[/b] automatically select gems or include socket bonuses in the score at this time.\r\n\r\n[h3]Viewing a Group in 3D[/h3]\r\nClick on [b]View in 3D[/b] in the menu dropdown of the group\'s control bar to display a 3D model of the items and select the race and gender to display them on. Of course, items which do not have models, such as trinkets and rings, will not be displayed.\r\n\r\n[/tab]\r\n\r\n[/tabs]'),(NULL,NULL,0,'help=stat-weighting',0,2,'[menu tab=2 path=2,13,3]Can\'t find the answer you were looking for? Just [url=/?aboutus#contact]contact us[/url], or post on our [url=/?forums&board=1]forums[/url]! \r\n\r\n[pad]\r\n\r\n[tabs name=weights]\r\n\r\n[tab name=FAQ]\r\n\r\n[h3]How do weights work?[/h3]\r\nThe weighting system allows you to give a weight value to attributes that matter to you and applies your ratings to items in your search results. Each weight value is multiplied by an item\'s stat points and then added together to get the item\'s total score. This score is used to sort the results and display the highest scoring items.\r\n\r\nIf you decide that spell damage is worth twice as much as spell crit, you could add the weights as 2 and 1, 100 and 50, or any other numbers with the same ratio.\r\n\r\nPlease note that weights only work for [url=/?items=4]Armor[/url], [url=/?items=2]Weapons[/url], [url=/?items=3]Gems[/url] and [url=/?items=0]Consumables[/url]. \r\n[h3]What is the difference between weights and equivalency?[/h3]\r\nThe equivalency of two attributes describes how much one equals the other. You may find equivalency ratings that say something like 1 agility = 1.5 strength. This is [b]not[/b] the same as weight values; in fact, it\'s the exact opposite! Equivalency describes the ratio of the stats to each other, which can be used to derive the stat weights. In this example, an appropriate set of weights might be agility 3 and strength 2; this works out to agility being [i]1.5 times as valuable[/i] as strength. \r\n[h3]Is there a way to save a template that I have created?[/h3]\r\nThere sure is! You can save your stat weighting scales by going to the \'Preset\' dropdown menu, selecting \'custom,\' and then filling in your own weights. After you\'ve modified them to your liking, you can hit \'Save\' to give them a name so they can be used for future searches as well.\r\n\r\nWeights also carry over from one item list to another if you use the database menu, so going from a [url=/?items=2&filter=wt=51:48:49;wtv=83:67:58]weighted list of weapons[/url] to the [url=/?items=4&filter=wt=51:48:49;wtv=83:67:58]cloth armor listing[/url] will also maintain your current weight scale. \r\n[h3]Is it better to match sockets and gain the socket bonus, or use the best gems?[/h3]\r\nThe weighting system answers this for you automatically. It compares the score of matching gems plus the score of the socket bonus, to the score of the best gems it could put in that item. It will automatically put in the gems that result in the highest net rating, taking socket bonuses into account. When the socket colors are matched, the socket bonus text will be listed below the gems for each item. \r\n\r\n[h3]What are the default weight presets based on?[/h3]\r\nWe\'ve done a great deal of research, tracking down equivalence points for all of the classes. We\'d like to thank all of the hard-working theorycrafters at [url=http://elitistjerks.com/f47/t21302-theorycrafting_think_tank/]Elitist Jerks[/url], [url=http://forums.tkasomething.com/showthread.php?t=9542]TKA Something[/url], [url=http://shadowpanther.net/aep.htm]Shadow Panther[/url], [url=http://druid.wikispaces.com/Healing+Gear+List]The Druid Wiki[/url], [url=http://www.emmerald.net/]Emmerald[/url], [url=http://www.lootrank.com/wow/templates.asp]Lootrank[/url], [url=http://pawnmod.trenchrats.com/index.php]Pawn Mod[/url], and [url=http://www.codeplex.com/Rawr]Rawr[/url], as well as a host of threads on the World of Warcraft forums. They provided the inspiration for the weighted search and a starting point for our preset values.\r\n\r\n[/tab]\r\n\r\n[tab name=\"Helpful tips\"]\r\n\r\n[ul]\r\n[li]You can help us [b]improve[/b] our presets! Email your suggestions to [feedback].[/li]\r\n[li]Don\'t weight stats that your character is [b]already capped on[/b] (e.g. Hit rating). Be sure to tweak the presets as needed![/li]\r\n[li]You can adjust a preset by clicking on the \'show details\' button.[/li]\r\n[li]Once you have generated a weighting you like, you can bookmark that page. Then, if you browse around on other pages using the menus at the top, your weight scale will be applied to that page as well.[/li]\r\n[/ul]\r\n\r\n[/tab]\r\n\r\n[tab name=Why?]\r\n\r\n[h3]Why does it give a higher score to 2H weapons over 1H weapons, when using a 1H + OH is better?[/h3]\r\nThe scores are based off the stat weights of the item by itself. Two-handers rank higher because by themselves they do have better stats than a one-hander with nothing else in the off hand. If you add up the scores of a main hand and off hand item, the total score is what you should use to compare to that of a two-hander. We do not assume a score for your offhand item, as there is no way of knowing what you have or can obtain for that slot unless you do a weighted search for it. \r\n[h3]Why does the preset list X as more important than Y?[/h3]\r\nSome attributes come in unusual value ranges on items, which affects their equivalency to other stats. It does not mean that your should focus on or ignore that stat, but that a single point of it is worth more or less compared to other stats. Stats with high number ranges (armor, weapon damage, penetration, etc) will require smaller weight values, while stats with low number ranges (mana regeneration) will require much larger weight values.\r\n\r\nIn essence, giving mana regeneration a score of 100 and healing a score of 25 does [b]not[/b] say that mana regeneration is more important than healing, simply that each point of mana regeneration is the equivalent of 4 points of healing.\r\n[h3]Why don\'t you have a preset for PvP/Tier 6 Raiding/...? Why doesn\'t your preset give a stat value for X?[/h3]\r\nIf you would like to suggest changes to the existing presets or new presets for other specs or situations, please do so to [feedback]. \r\n[h3]Why doesn\'t the preset limit the items to X, Y, and Z?[/h3]\r\nThe weight presets are for sorting; filters are for limiting the search results. If you want to restrict the items you see, use the appropriate tool - the filter options. The only limit applied by the weight scales is that it will not display items with a score of 0 or less. You should continue to use the existing filtering system if you want to see items of a specific type, slot, source, speed, etc.\r\n[h3]Why does it suggest the gems it does for the sockets?[/h3]\r\nThe suggested gems are based on your weights. If you would like to see a different gem in the sockets, try increasing the weight of the appropriate stat. If you feel the weights in the presets need to be adjusted, please let us know at [feedback].\r\n\r\n[/tab]\r\n\r\n[/tabs]'),(NULL,NULL,0,'help=screenshots-tips-tricks',0,2,'[menu tab=2 path=2,13,2]\r\n\r\nWe thrive on user contributions! Quest data, database comments, forum posts - you name it, we love it! One of our favorite methods of contribution is via uploaded [b]screenshots[/b], images depicting various items, NPCs or quest details in the World of Warcraft. Users can submit screenshots to any database page which will then be reviewed by our staff and, upon approval, added to a database page! Taking and uploading screenshots is easy!\r\n\r\n[small]The information below is graciously provided by [url=http://us.blizzard.com/support/article.xml?locale=en_US&articleId=21048]Blizzard Support[/url].[/small]\r\n[h3]Taking Screenshots on Windows[/h3]\r\n[ul]\r\n[li]While in the game, press the Print Screen key on your keyboard.[/li]\r\n[li]You should see a \"Screen Captured\" message.[/li]\r\n[li]The screenshot will appear as a .JPG file in the Screenshots folder, in your main World of Warcraft directory.[/li]\r\n[li]You should be able to double click on the screenshot files to view the screenshots in Windows default image viewer.[/li]\r\n[/ul]\r\n\r\n[b]Extra notes for Windows Vista users[/b]\r\n[ul]\r\n[li]Due to extra security on the system the screenshots will be saved to the following folder:C:\\\\users\\\\*your user name*\\\\AppData\\\\Local\\\\VirtualStore\\\\Program Files\\\\World of Warcraft\\\\Screenshots[/li]\r\n[li]You may also have to turn on the ability to view hidden files as the AppData folder may be hidden.\r\n[ul]\r\n[li]Click the Start/Window button, select Control Panel, Appearance and Personalization, Folder Options.[/li]\r\n[li]Next click on the View tab, under the Advanced settings, click Show hidden files and folders, and click OK to finish.[/li]\r\n[/ul][/li]\r\n[/ul]\r\n\r\n[h3]Taking Screenshots on Mac[/h3]\r\n[ul]\r\n[li]Players can take a screenshot in-game using the keyboard key bound to the Print Screen functionality.[/li]\r\n[li]If you have a keyboard with an F13 key, press the key to take an in-game screenshot. Players without an F13 key on the keyboard can change the default Screen Shot key in the Key Bindings menu.[/li]\r\n[li]You should see a \"Screen Captured\" message.[/li]\r\n[li]The screenshot will appear as a JPEG file in the Screenshots folder, in your main World of Warcraft folder.[/li]\r\n[/ul]\r\n\r\nRemember to turn off your in-game UI using the Alt+Z (or ⌘+V) command! Upon taking your screenshot, you can then go in and use an image editor (such as the free program [url=http://www.getpaint.net]Paint.NET[/url]) to crop your image for faster upload. You can select specific sections of a screenshot to upload (if you are featuring a particular piece of armor, for example) and save the file, then simply upload your pre-cropped image directly! If not, you can easily crop your screenshot after uploading but before submitting using our handy tool.\r\n\r\nTo submit a screenshot, simply navigate to the database entry for which you\'ve taken a screenshot and navigate to the \'Contribute\' section. Select the \'Submit a screenshot\' tab and click \'Choose file\' to locate the file on your system. Remember that only PNG and JPG file types are accepted! Once you have selected the screenshot simply click \"Submit\" and you\'re on your way! You will then be able to crop the image if necessary before your image is finally submitted for review. Upon approval (which may take up to 72 hours) your screenshot will then be featured on the database page, as well as in a \'Screenshots\' tab in your user profile!\r\n\r\n\r\n[h2]Quality Tips[/h2]\r\n\r\n[screenshot url=STATIC_URL/images/help/screenshots/hinterlands.jpg thumb=STATIC_URL/images/help/screenshots/hinterlands2.jpg float=right]The Hinterlands[/screenshot]A good screenshot is like a miniature piece of art. It should showcase the main object, but take into account the details around it. The same 7 elements of art design come into play here, Line, Shape, Form, Space, Texture, Light & Color. We\'ll touch on several of these and how to make use of the in game settings and mechanics to enhance your pictures.\r\n\r\nTurn your resolution and color sampling as high as your computer can handle. Turn on all the image effects and details, but turn down the weather effects to the lowest setting. In general you want all your glow and spell effects maxed to really show the environment to its fullest potential (they actually help with the lighting too!) You may find a shot that you need to play with these settings to enhance, sometimes turning down environmental detail is helpful to remove extra grasses.\r\n\r\nWorld of Warcraft actually has an internal setting for screenshot quality, and by default that quality is set to [b]3/10[/b]. You can turn this up, though, in order to take higher quality screenshots. In order to do so, type this command into your chatbox:\r\n\r\n[code]/console screenshotQuality 10[/code]\r\n\r\nMost of the time taking the pictures from 1st person view works best, so zoom all the way in so that you\'re looking through your character\'s eyes. Occasionally the object might be too big (large NPCs especially) to use this view - if this is the case get as close to them as you can without having your body in the shot and swing the camera around to get the angle that you\'re looking for.\r\n\r\nPay attention to the light - a well lit picture is 10 times better than a dark one. You may even want to do a little color correcting before uploading - increase the brightness and contrast a touch. For instance - it\'s a lot easier to take pictures in sunny Stormwind than deep in the mountains of torch lit Ironforge. Daytime pictures also turn out better than night.\r\n\r\n[h3]Featuring Armor[/h3]\r\n\r\n[screenshot url=STATIC_URL/images/help/screenshots/armor.jpg thumb=STATIC_URL/images/help/screenshots/armor2.jpg float=right]Dreamwalker Spaulders[/screenshot]We want to see the armor! Not Joe Schmoe in the armor. In general you want close ups of the piece itself (except for full set pictures). Don\'t be afraid to submit a 4 inch picture of one glove. Once\'s it\'s cropped and loaded and shrunk down to the thumbnail it will look great!\r\n\r\nUse your best judgment when cropping armor pics, but remember - we want to see details of the armor - not the person or a far away image. Of course, this also applies to weapons or any other piece of equipment!\r\n\r\n[h3]Featuring NPCs[/h3]\r\n\r\n[screenshot url=STATIC_URL/images/help/screenshots/npc.jpg thumb=STATIC_URL/images/help/screenshots/npc2.jpg float=right]Cairne Bloodhoof [/screenshot]Full body shots should be the norm. If you can\'t get a good full shot (e.g. they\'re standing behind a counter) get the waist up shot. There\'s no need to include the on-screen text and titles of NPCs. The website already lists those, so just get in close and take a great shot of the NPC itself.\r\n\r\nGet down on their level - you may need to \"/sit\" or even \"/sleep\" to get a good view of something low to the ground (scorpions, boots, spiders, etc.)\r\n\r\nWhen capturing moving NPCs, try to get as much a head on front shot as you can, being willing to take a few hits while you take picture of a mob attacking you can make for a great shot. If you don\'t want to get your hands dirty, sitting in place for a while and waiting for it to path in front of you is often easier and faster than running around it trying to get your shot.\r\n\r\nTalking to friendly NPCs will usually make them face you - you can then spin around and get the best background for your picture. You may also catch them in an interesting motion or gesture.'),(NULL,NULL,0,'help=profiler',0,2,'[menu tab=2 path=2,13,6]Can\'t find the answer you were looking for? Just [url=/?aboutus#contact]contact us[/url], or post on our [url=/?forums&board=1]forums[/url]!\r\n\r\n[pad]\r\n\r\n[tabs name=profiler]\r\n\r\n[tab name=\"Browsing characters\"]\r\n\r\n[div float=right align=right][img src=STATIC_URL/images/help/profiler/menu.gif]\r\n[small]Navigating the menu to your battlegroup and realm.[/small][/div]We maintain a database of [i]millions[/i] of [url=http://www.wowarmory.com/]Armory[/url] characters, guilds, and arena teams that have been imported by our users. You can browse through this extensive list by visiting the main [url=/?profiles]profiles[/url] page and selecting a region, battlegroup, or realm from the menus at the top.\r\n\r\nThis will give you an unfiltered look at the players and guilds in the area you selected, with the most recently updated characters displayed first. You can also enter your characters name in the box at the top to jump directly to that character.\r\n\r\n[h3]Finding My Characters[/h3]\r\n\r\n[ul]\r\n[li]Use the breadcrumb listings at the top to browse to your region, battlegroup, and realm. When you do this, a box will appear in the listing at the top of the page. Enter your character\'s name in this box to be taken directly to your character. You can use the \"Claim Character\", which is located under the Manage Character button, to save a character to your [url=/user=fewyn#characters]user page[/url] for later viewing.[/li]\r\n[/ul]\r\n\r\n[i]Tip: Claimed characters can be made public or private as you choose—so you only show off the characters people want you to see! Basic information for the profiles will remain public, just as it is in the Armory—but any connection to your account will be hidden.[/i]\r\n\r\n[h3]Filters[/h3]\r\nBut that\'s not the only way to find a character! You can also search Profiles using our robust filter system, just the same way that you can search items, NPCs, or spells in game. Characters and guilds can be filtered by name, region, and realm to limit the number of displayed results.\r\n\r\nAdditionally, characters can be filtered by faction, level, race, and class – as well as a number of other unique and useful criteria. For example:\r\n\r\n[ul]\r\n[li][div float=right align=right][img src=STATIC_URL/images/help/profiler/filters.gif]\r\n[small]Searching for characters that match your criteria.[/small][/div]Let\'s see [url=/?profiles=us.draenor&filter=cl=8;ra=11;cr=35;crs=0;crv=450]all the Draenei mages on my server that have their tailoring maxed out[/url].[/li]\r\n[li]Hmm... I wonder if anyone is [url=/?profiles=eu&filter=na=Malgayne]using my name on European servers[/url]?[/li]\r\n[li]How do I compare to [url=/?profiles=us.draenor&filter=cl=2;minle=80;maxle=80;cr=7;crs=1;crv=50]other Retribution-specced paladins on my server[/url]?[/li]\r\n[li]How many [url=/?profiles&filter=cr=23;crs=0;crv=871]Bloodsail Admirals[/url] are there out there?[/li]\r\n[li]Who got caught wearing a [url=/?profiles&filter=cr=21;crs=0;crv=22279]Lovely Black Dress[/url]?[/li]\r\n[li]How many people on my server and faction [url=/?profiles=us.sentinels&filter=si=2;cr=23;crs=0;crv=2904]completed Heroic Ulduar[/url]?[/li]\r\n[/ul]\r\n\r\nWe\'ll be adding more filters as time goes on, so feel free to experiment – and let us know if you think of other ideas!\r\n\r\n[pad][pad][pad]\r\n\r\n[h3]Guild and Arena Team Rosters[/h3]\r\nWhen you click on a character\'s guild or arena team, you will be directed to a roster view listing all the characters that belong to it. The roster view displays additional information, including guild ranks and personal arena team ratings. You can further filter this information using the [b]Create a filter[/b] link, should you want to find characters matching specific criteria. Now its easy to find all of the crafters in your guild!\r\n\r\n[h3][img src=STATIC_URL/images/help/profiler/queue.gif float=right]Resync Queue[/h3]\r\nWhen a character resync is requested, it is added to the queue. The queue is used to make sure everyone\'s characters are updated and processed in the order they were submitted, without overloading the [url=http://us.battle.net/wow/en/]Battle.net Armory\'s API[/url] with requests. Whenever you access a character that does not exist in our database or has not been updated in more than 1 hour, it will automatically be added to the queue.\r\n\r\n[/tab]\r\n\r\n[tab name=\"General usage\"]\r\n\r\nThe profiler has a wealth of information it can display about characters and custom profiles, so it can seem daunting at first! Each of the sections are broken down in detail below.\r\n[h3]Basic Profile Information[/h3]\r\nAt the top of a profile you will see an expanded header with vital information about the profile itself. All profiles have an icon and the character\'s race, class and level; Armory characters display a link to the character\'s guild under the name, while custom profiles display a description set by the user that created it. A link to [b]Edit[/b] this information appears on the bottom line, allowing you to update a profile you created or make a new custom profile from an existing one.\r\n\r\n[ul]\r\n[li][img src=STATIC_URL/images/help/profiler/edit.gif float=right][b]Name [/b]– Give your profile a name! Names must start with a letter, and can only contain letters, numbers, and spaces.[/li]\r\n[li][b]Level[/b] – Select a level for your profile. Profiles must be at least level 10 (55 for Death Knights) and no more than level 85.[/li]\r\n[li][b]Race[/b] – Ever wonder what you\'d look like as a tauren instead of an orc? Choose any race for your profile, and the character model with automatically be updated.[/li]\r\n[li][b]Class[/b] – You can select any class you like, regardless of racial restrictions. See what your stats would be if you were a draenei druid![/li]\r\n[li][b]Gender[/b] – Select male or female to set your character\'s gender.[/li]\r\n[li][b]Icon[/b] – Icons are automatically generated for Armory characters and in game class/race combinations, but you can change the icon to any you like.[/li]\r\n[li][b]Description[/b] – Enter a tag line or brief description for the profile so you and others know what it is about.[/li]\r\n[li][b]Visibility[/b] – Public profiles will be visible on your user page and anyone can view a public profile. Private ones will not be displayed or visible to others.[/li]\r\n[/ul]\r\n[i]Note: If you edit a character in any way, it will become a custom profile. The reputations, achievements, and raid progress information will be removed.[/i]\r\n\r\n[h3]Managing Profiles[/h3]\r\nIn the upper right are a number of useful buttons for managing profiles without having to go back to your user page. Each of the buttons have several options that can be used to manage the character\'s page you are currently on and include the following options.\r\n\r\n[ul]\r\n[li][b]Custom Profile[/b]\r\n[ul][li][b]New[/b] – This is a quick link to creating a new, blank profile from scratch. It will open in a new window so you do not lose your current profile. This option is always available.[/li]\r\n[li][b]Save[/b] – Save any changes you have made to this profile. This option is only available for logged in users on profiles they own.[/li]\r\n[li][b]Save as[/b] – This will let you save your current changes under a new name. It is extremely useful for making copies of profiles! This option is only available for logged in users.[/li][/ul][/li]\r\n[li][b]Manage Character[/b]\r\n[ul][li][b]Resync[/b] – Request that the character be updated from the armory; it will be added to the queue. This option is only available on Armory character pages.[/li]\r\n[li][b]Claim character[/b] – Adds an Armory character to your user page. This is a good thing to do with all your alts. This option is only available for logged in users on Armory character pages.[/li]\r\n[li][b]Remove[/b] - Removes the character from your user page. Use this if you no longer play the character or have long since deleted it.[/li]\r\n[li][b]Pin/Unpin[/b] - Pin one of your characters so you can perform personalized searches throughout the database for missing or completed quests, achievements, recipes and more![/li]\r\n[/ul][/li]\r\n[/ul]\r\n\r\n[h3]From the User Page[/h3]\r\n[img src=STATIC_URL/images/help/profiler/userpage.gif float=right]All of your claimed Armory characters and custom profiles are listed in one convenient place on your user page. From the [b]Characters[/b] tab you can remove one or more claimed characters. The [b]Profiles[/b] tab allows you to create a new profile, delete profiles, or change the visibility settings of profiles. Your private profiles will not be visible to anyone else.\r\n\r\n[i]Tip: When you are logged in, all of your characters and custom profiles can be accessed from the [b]My profiles[/b] menu at the top right of any page![/i][pad]\r\n[h3]Saving Your Work[/h3]\r\nAny profile can be edited, even if you don\'t own it, but you\'ll probably want to save your work when you\'re done! You must have an account with us in order to save a profile. Once you\'ve created an account, you can bookmark any number of Armory characters and save up to 10 custom profiles. Premium users will be able to create even more, so upgrade if 10 just isn\'t enough! You can use the red buttons to save a profile from its page, and manage your existing profiles and characters from your user page. \r\n\r\n[/tab]\r\n\r\n[tab name=\"Inventory and talents\"]\r\n[img src=STATIC_URL/images/help/profiler/character.jpg height=300 float=right]The main tab for a profile is the character inventory, which includes a lot of the same information you would see by looking at your character pane in game. This tab is broken up into four key sections - the character view, quick facts box, statistics, and gear summary.\r\n\r\n[h3]Character View[/h3]\r\nThe first thing you\'ll notice, of course, is your character – as rendered by our custom built modelviewer, in all it\'s three-dimensional glory. You can turn the character with your mouse, and zoom in and out using the A and Z keys, just like the modelviewer elsewhere in the site. [b]We even pull your face, hair, and skin color information from the Armory![/b]\r\n\r\nOn either side of the character are inventory icons which you can right click on for a menu of options:\r\n\r\n[i]Tip: You can remove a gem or enchant by clicking None in the picker window or by right clicking on it in the gear summary.[/i]\r\n\r\n[ul]\r\n[li][img src=STATIC_URL/images/help/profiler/itemmenu.gif float=right][b]Equip... / Replace...[/b] – Selecting this option will give you a quick search box in which you can type an item\'s name. Click on the item or hit return to equip it.\r\nUnequip – Unequips the item, of course. :)[/li]\r\n[li][b]Add / Replace enchant...[/b] – The spell icon on the left shows if the item is enchanted. This opens a customized picker window with all enchants available for the item slot.[/li]\r\n[li][b]Add / Replace gem...[/b] – The icon on the left shows the socket color or socketed gem. Like the enchants, this opens a picker window with valid gems for the socket.[/li]\r\n[li][b]Extra socket[/b] – The check mark on the left indicates if a blacksmithing socket has been added to this item. Click to toggle on or off.[/li]\r\n[li][b]Clear Enhancements[/b] - This will remove all reforges, enchantments, gems and extra sockets from an item. Useful if you want to start fresh with an item.[/li]\r\n[li][b]Display on character[/b] – The checkmark on the left indicates if the item is displayed on the model. Click to toggle on or off – it works for more than just cloaks and helms![/li]\r\n[li][b]Compare[/b] – Adds the item to the [url=/?compare]item comparison tool[/url] and opens it in a new window to compare with other items.[/li]\r\n[li][b]Find upgrades[/b] – Uses our [url=/?help=stat-weighting]weighted search[/url] to find upgrades based on your talent spec.[/li]\r\n[li][b]Who wears this?[/b] – Creates a filtered list of other Armory characters who are also wearing the item.[/li]\r\n[/ul]\r\n\r\n[i]Tip: Items that can take enchantments but have no enchantment, or which have empty sockets, will even have a little notification in the tooltip![/i]\r\n\r\n[img src=STATIC_URL/images/help/profiler/quickfacts.gif float=right][h3]Quick Facts Box[/h3]\r\nOn the right hand side is a handy Quick Facts box that displays basic, defining information about a profile. This box is chock full of useful information, including talent spec, achievement points, and professions.\r\n\r\n[i]Tip: Any raid icon that\'s ringed in [color=c4]gold[/color] is a raid that the character has cleared![/i]\r\n[h3]Statistics[/h3]\r\nYou\'ll also notice that all of a profile\'s statistics are laid out beneath the character view. This is also all information you can get from the Armory (and then some), but we lay it out in a nice, convenient page so you can view it all at once – no more messing with drop down menus. You can also click on a statistic and expand it so you can see its tooltip information right there on the page—or click on the header to expand all the related statistics. Your statistics are updated as you edit any part of a profile, including race, class, level, items, enhancements, or talents – all in real time! [b]Statistic modifications from glyphs and buffs are not presently supported, but will be in the future.[/b]\r\n\r\n[i]Note: These statistics are calculated manually – they are not pulled from the Armory. Statistics calculations are still in beta and will ironed out as we go.[/i]\r\n\r\n[img src=STATIC_URL/images/help/profiler/statistics.gif float=center]\r\n\r\n[h3]Gear Summary[/h3]\r\n[div float=right align=right][img src=STATIC_URL/images/help/profiler/gearsummary.gif]\r\n[small]A warning message is displayed for missing enhancements.[/small][/div]Last on the character inventory tab, but not least, is the gear summary. This is a personalized list of all items worn by the character, with convenient column headers and in line filtering options. Use it to see where most of a character\'s items come from, what is the best and worst piece, and whether or not there are missing gems and enchants. Just in case the empty icons aren\'t clear enough, a warning appears at the top of the list if a character is missing gems, enchants, or blacksmith sockets. This [color=q10]warning[/color] is based on the professions of the character if it is an Armory profile, and otherwise shows you everything missing on custom profiles.\r\n\r\nThe gems and enchants can also be edited from within the gear summary, and have a few additional options not available in the character view. You can remove or replace an enhancement from here, and you can find upgrades using our [url=/?help=stat-weighting]weighted search[/url] – just like items!\r\n\r\n[h3]Talents[/h3]\r\nThe talents tab includes an inline version of our [url=/?talent]talent calculator[/url] with a full display of a character\'s talents. It is locked by default, but you can unlock it to begin editing talents, just as you would normally. There are two extra features in the Profiler\'s talent calculator: you can store and swap between two specs for each character, and export the current talent build to the calculator to link to your friends. When you change your talents (or swap between specs) your gear score and statistics will be updates real time!\r\n\r\n[/tab]\r\n\r\n[tab name=\"Other tabs\"]\r\n\r\n[h3]Reputation[/h3]\r\nThe reputation tab displays the complete faction information of an Armory character, with collapsible headers for each section. Its much easier to read than the tiny faction pane in game! Of course, you can link directly to the faction\'s page to get more information about that faction. \r\n[h3][img src=STATIC_URL/images/help/profiler/achievements.gif float=right]Achievements[/h3]\r\nThe achievements tab lists an Armory character\'s progress in each of the main achievement categories, and has a filterable list of achievements including date completed. All of the normal column and list filters are available, along with some new ones! You can filter the list by earned, in progress or complete achievements – complete are displayed by default – or click on any of the category progress bars to only display achievements from that category.\r\n\r\n[/tab]\r\n\r\n[tab name=Completion_Tracker]\r\n\r\n[img src=STATIC_URL/images/help/profiler/quests.jpg float=right width=450]You can use the Profiler\'s [b]Completion Tracker[/b] feature to keep track of your quests, achievements, pets, mounts, recipes, and more!\r\n\r\n[h3]Getting Started[/h3]\r\n\r\nIn order to start tracking your completion data, all you need to do is visit your character\'s page on the profiler and resync it. This will automatically collect data about your character\'s completed achievements, companion pets, mounts, quests, recipes, reputations and titles.\r\n\r\n[h3][img src=STATIC_URL/images/help/profiler/completion.jpg float=right]Tracking Your Completion Data[/h3]\r\n\r\nOnce you\'ve got your data up on the site, it will be available in the form of five new tabs: [b]mounts[/b], [b]companions[/b], [b]recipes[/b], [b]quests[/b], and [b]titles[/b].\r\n\r\nIf you open the mounts, companions, or titles tabs, you\'ll immediately be greeted by a list of all the entries you\'ve already completed. You can cycle through the different tabs to see the ones you already have, the ones you still have yet to collect, a complete list, or a list of just the ones you\'ve \"excluded\" (more on that shortly). You can also use the \"Search within results\" box to search the list based on a keyword, just like you can with other search results in the database.\r\n\r\nThe recipe, and quest tabs, like the Achievements tab, contain more entries—so you\'ll be presented with a box like the one shown above. From there, all you have to do is click one of the progress bars to see the complete tabbed list in each category.\r\n\r\n[h3]Exclusions[/h3]\r\n\r\nWhen you\'re trying to make sure we check off every quest, achievement, or mount on our list, everyone knows that there are some that you just don\'t want to bother with. To that end, we\'ve created [b]exclusions[/b].\r\n\r\n[img src=STATIC_URL/images/help/profiler/exclusions.jpg float=right]Using exclusions, you can flag certain quests, mounts, achievements, recipes, pets, or titles that \"don\'t count\" toward your completion total. When you exclude (for example) a quest, that quest no longer appears in \"incomplete\" listings, and the total number of quests in that category is reduced by one.\r\n\r\n[b]For example:[/b] There are 632 quests in the \"Eastern Kingdoms\" category. If I were to decide that [quest=367] is for noobs and I don\'t want to count it, then all I have to do is put a check in the box next to the quest and click \"Exclude\". After I do so, the Eastern Kingdoms progress bar will only show [i]631[/i] quests total—the remaining quest will appear in the \"Excluded\" tab but won\'t be counted for anything else.\r\n\r\nIf you want to re-include a quest, just go to the \"Excluded\" tab and then use the checkboxes to restore as many as you like. You can do the same thing for achievements, titles, mounts, pets, or recipes.\r\n\r\nIf you [b]complete[/b] a quest that you have excluded, it will show in the progress bar as a [b]+1[/b]. Example: If there are 31 quests in the \"Miscellaneous\" category, and I\'ve completed 20 quests and excluded 1, the progress bar will show [b]20/30[/b]. If I have completed [i]the quest that I excluded[/i], then the progress bar will show [b]20(+1)/30[/b]. If I then go on to complete ALL the quests in that category (including the one I excluded), the progress bar will show [b]30(+1)/30[/b].\r\n\r\n[b]Exclusion Manager[/b]\r\nThe companions and mounts tabs let you manage your exclusions en masse with the Exclusion Manager. Just click the \"Manage Exclusions\" button on top of the tabs to see a list of convenient categories you might want to exclude. There\'s also a \"reset all\" button here to let you wipe all of your exclusions and start over.\r\n\r\n[b]Note:[/b] The Exclusion Manager is currently only available for companions and mounts.\r\n\r\n[i]Tip: Exclusions are tied to your account, not to a particular character. This is so even when you look at someone else\'s character, you\'re judging them by [/i]your[i] completion standards, not anyone else\'s![/i] \r\n\r\n[/tab]\r\n\r\n[tab name=Calculations]\r\n\r\nMost of the information we display is pretty straightforward. A lot of it, particularly the stats on items, is readily available in our database and on various tooltips. There are some new numbers on profile pages that you may ask, what does this number mean? How was it calculated?\r\n[h3]Base Statistics[/h3]\r\nA character\'s five base statistics are determined primarily by his or her class and level. This base amount has a modifier applied to it depending on the character\'s race. We gathered an extensive amount of data from the armory to come up with these base numbers, using untalented individuals of every race, class, and level combination. Because racial modifiers are consistent, we are able to create statistics for \"fake\" race and class combos using the data we already know. However, the Armory does not give data on characters below level 10 or Death Knights below level 55, so we have no statistic information for these profiles. To simplify things, we have set a minimum level for custom profiles based on the available statistics.\r\n[h3]Gear Score[/h3]\r\nOkay, so a lot of sites have gear scores. Most of them (ours included) are based around the [url=http://www.wowwiki.com/Item_level]item budget[/url] Blizzard uses to determine how much of each stat can be on an item. This budget is calculated using the item\'s level, quality, and slot, and we use the budget as the item\'s gear score. You can view a complete breakdown of an item\'s gear score by mousing over it in the [url=/?help=profiler#profiler-inventory-and-talents]gear summary[/url] at the bottom of the character tab. You can view a breakdown of a profile\'s total gear score by mousing over it in the Quick Facts box, also on the character tab.\r\n\r\nEach gear score is color coded based on the item levels of the gear in reference to the character level. [b][color=q0]Grey[/color][/b] for poor, [b][color=q1]White[/color][/b] for common, [b][color=q2]Green[/color][/b] for uncommon, [b][color=q3]Blue[/color][/b] for rare, [b][color=q4]Purple[/color][/b] for epic and [b][color=q5]Orange[/color][/b] for legendary. For example, a level 70 character wearing high item-level, raiding epics from [zone=3606] and [zone=3959] will have a purple-colored gearscore, as their items are considerably \"epic\" quality for their level. However, the same character at 80, if wearing this same gear, will have the gearscore colored blue as the items are of lower-than-optimal quality for their level.\r\n\r\nThe value of an empty socket was generated using the gear score of appropriate gems for the item in question, and subtracted from the item\'s score. This allows us to score unsocketed items lower than an item without sockets of the same level, quality, and slot. Items with better than expected gems will receive higher scores, and items with lower quality gems (or no gems at all) will receive lower scores.\r\n\r\nThe values of enchants are based off of the level of the enchantment. Endgame enchantments are 20 points, profession perks are 40 points, etc. The numbers go down from there.\r\n\r\nYou may notice that some profiles have different gear scores for the same item. There is an extreme difference in budget between a two-handed or one-handed weapon, which causes a discrepancy in scores between characters who should be fairly equal according to the level of their gear. To address this, the gear score of weapons has been normalized so that a character with appropriate weapon choices has the equivalent score of two two-handed weapons. Appropriate weapons are determined by your class and spec; for example, an enhancement shaman should dual wield one handed weapons, a protection warrior should have a one-hander and shield, etc. For classes which the melee weapons don\'t really matter – like hunters or spellcasters – anything they can use is considered appropriate.\r\n\r\n[i]Note: Gear score does not take into account the stats of the item. It is a measurement of quality of gear, not whether the stats on the gear are suited to the character\'s spec.[/i]\r\n\r\n[h3]Guild Scores[/h3]\r\nGuild gear scores and achievement points are derived using a weighted average of all of the known characters in that guild. Guilds with at least 25 level 80 players receive full benefit of the top 25 characters\' gear scores, while guilds with at least 10 level 80 characters receive a slight penalty, at least 1 level 80 a moderate penalty, and no level 80 characters a severe penalty. This is to prevent small guilds and bank alts from appearing to have higher scores than legitimate raiding guilds. Instead of being based on level, achievement point averages are based around 1,500 points, but the same penalties apply.\r\n\r\n[/tab]\r\n\r\n[/tabs]'),(8,577,0,NULL,0,2,'[minibox]\n[h2]Steamwheedle Cartel[/h2]\n[faction=21]\n[faction=577]\n[faction=369]\n[b]Everlook[/b]\n[/minibox]\n\n[b]Everlook[/b], the faction of the town Everlook, is a trading post is run by the goblins of the Steamwheedle Cartel. It lies at the crossroads of [zone=618]\'s main trade routes.\n\n[h3]General Information[/h3]\nThis town is the last point of civilization before reaching Hyjal Summit. It is run by goblins as a trading post and is officially neutral to all races and factions. Even so, pilgrims allowed to venture up to the World Tree stop here, but otherwise this is the highest that merchants and explorers may venture without the night elves’ permission. Everlook would offer a commanding view of Kalimdor, if it were not at such a high altitude that clouds constantly shroud the mountain’s lower flanks.\n\nEverlook is the only major goblin outpost in northern Kalimdor, and it serves several purposes. First, it serves as the base of operations for goblin thorium and arcanite miners since Winterspring has some of the few untapped veins of those materials on the continent. Second, it serves as a center of trade between the Alliance and the Horde. While Everlook is hardly as safe as Moonglade, generally the Alliance and the Horde treat each other fairly well there. Additionally, Everlook is a frequent stop-off and resupply point for the faithful who make the pilgrimage through Winterspring to Hyjal Summit.\n\n[h3]Reputation[/h3]\nReputation for Everlook and the Steamwheedle Cartel is mostly gained from quests in Winterspring. Having a friendly or higher reputation will make the guards help you in case of initiated violence against you.'),(NULL,NULL,0,'help=talent-calculator',0,2,'[menu tab=2 path=2,13,4]Can\'t find the answer you were looking for? Just [url=/?aboutus#contact]contact us[/url], or post on our [url=/?forums&board=1]forums[/url]! \r\n\r\n[toc]\r\n\r\n[h2]General Usage[/h2]\r\n[ul]\r\n[li][screenshot url=STATIC_URL/images/help/talent-calculator/glyphs.jpg thumb=STATIC_URL/images/help/talent-calculator/glyphs2.jpg width=268 height=218 float=right][/screenshot][b]Selecting a class[/b] - Easily select a class\' talent tree by chosing from the class icon at the top, or from the dropdown menu. Clicking on a class\' name at the top left of the calculator will open that class\' page here on on this site, providing even more detailed information![/li] \r\n[li][b]Adding or removing talent points[/b] - To add points in a talent simply click the appropriate talent. To remove points, you can either right-click (or Shift+click) the talent.[/li]\r\n[li][b]Adding glyphs[/b] - Click on an empty glyph slot to open a picker window from which you can make your selection. To remove a glyph, simply right-click (or Shift+click) that glyph.[/li]\r\n[li][b]Linking to a build[/b] – Simply copy the auto-updating URL from your browser\'s address bar.[/li]\r\n[/ul]\r\n\r\n[h2]Tools + Options[/h2]\r\n[ul]\r\n[li][b]Reset all[/b] - Resets all talents across all trees.[/li]\r\n[li][img src=STATIC_URL/images/help/talent-calculator/options.jpg float=right][b]Reset tree[/b] - Clicking the red X at the top right corner of a talent tree will reset all talents in that particular tree. Other trees will not be reset.[/li]\r\n[li][b]Lock / Unlock[/b] - Locks or unlocks the talent build, preventing (or allowing) changes to be made. Linking to a build will automatically lock talents.[/li]\r\n[li][b]Import[/b] – Displays a pop-up text window where you can enter the URL of a talent build made with [url=http://www.wowarmory.com/talent-calc.xml]Blizzard\'s talent calculator[/url]. Be sure that you first select the \"Link to this build\" option in the Blizzard talent calculator so that the URL will be properly formatted for importing.[/li]\r\n[li][b]Print[/b] - Opens up a new, printer-friendly page with a textual representation of your chosen talents. Nice if you want to paste the talents you\'ve chosen somewhere, and would prefer it written out.[/li]\r\n[li][b]Link[/b] - Locks your chosen talents and creates a link to your build. Use this option to easily create a URL to share your build with others![/li]\r\n[/ul]\r\n\r\n[h2]Useful Tips[/h2]\r\n\r\n[ul]\r\n[li]When the calculator is locked, you can click talents and glyphs to view their corresponding spell or item page.[/li]\r\n[li]If you\'re building a third-party application, you can link to our talent calculator by using Blizzard-style URLs such as:\r\n[code]HOST_URL?talent#hunter-512002015051122431005311500053052002300100000000000000000000000000000000000000000[/code][/li]\r\n[/ul]'),(NULL,NULL,0,'help=modelviewer',0,2,'[menu tab=2 path=2,13,1]\r\n\r\n[url=item=35350][img src=STATIC_URL/images/help/modelviewer/ss-viewin3d.gif float=right][/url]Aowow has a model viewer that will let you see the items and NPCs in the game in full 3D!\r\n\r\nYou can use the dropdown menus to select which character model you want to display armor pieces on, and the model viewer will remember your choice.\r\n\r\nThere are two different versions of the model viewer available, one written in Flash, and the other one written in Java. Aowow should remember which version you used last time, and will automatically open that model viewer the next time you click on the \"View in 3D\" button.\r\n\r\nIf you have any issues, please report them [url=/?forums&topic=202524]here[/url]!\r\n\r\n[i]Tip: You can close the box by clicking anywhere outside of the box.[/i]\r\n\r\n[h2]Modes[/h2]\r\n\r\n[tabs name=mode]\r\n\r\n[tab name=Flash]\r\n\r\n[url=item=34092][img src=STATIC_URL/images/help/modelviewer/ss-flash.png float=right][/url]The [b]Flash[/b] viewer is simple, quick to load, and should work on nearly all browsers. The Flash viewer is the default viewer, and all models will automatically load in the Flash Viewer unless you specify otherwise.\r\n\r\nIt requires the latest version of [url=http://www.adobe.com/go/BONRN]Flash[/url] to be installed on your computer.\r\n\r\n[h3]Controls[/h3]\r\n[ul]\r\n[li][b]Rotate[/b] – Click and drag / arrow keys[/li]\r\n[li][b]Zoom[/b] – Mousewheel / A & Z keys[/li]\r\n[/ul]\r\n\r\n[h3]Features[/h3]\r\n[ul]\r\n[li]Motion blur[/li]\r\n[li]Full screen mode[/li]\r\n[/ul]\r\n\r\n[/tab]\r\n\r\n[tab name=Java]\r\n\r\n[url=/?item=35350][img src=STATIC_URL/images/help/modelviewer/ss-java.png float=right][/url]The Java viewer is slower to initialize than the Flash Viewer, but once it\'s initialized it renders in [b]much greater[/b] detail. Most browsers will only need to initialize it once, and subsequent loads will be much faster. Some browsers may ask you to accept a security certificate when you initialize the viewer.\r\n\r\nIt requires the latest version of [url=http://jdl.sun.com/webapps/getjava/BrowserRedirect?locale=en&host=www.java.com]Java[/url] to be installed on your computer.\r\n\r\n[h3]Controls[/h3]\r\n[ul]\r\n[li][b]Rotate[/b] – Click and drag[/li]\r\n[li][b]Zoom[/b] – Mousewheel[/li]\r\n[li][b]Move[/b] – Right-click and drag[/li]\r\n[/ul]\r\n\r\n[h3]Features[/h3]\r\n[ul]\r\n[li]3D acceleration[/li]\r\n[li]Animations on NPCs, character models, small pets, and mounts[/li]\r\n[/ul]\r\n\r\n[/tab]\r\n\r\n[/tabs]\r\n'),(NULL,NULL,0,'tooltips',0,2,'[menu tab=2 path=2,10]\r\n\r\n[div float=right align=right][url=http://wow.joystiq.com/2010/04/14/breakfast-topic-using-irl-irl/][img src=STATIC_URL/images/help/tooltips/ss-wowcom.png][/url]\r\n[small]Tooltips in action on [url=http://wow.joystiq.com/2010/04/14/breakfast-topic-using-irl-irl/]WoW Insider[/url][/small][/div]\r\n\r\nIt\'s never been easier to add tooltips to your site.\r\n\r\n[ol]\r\n[li]Add this piece of HTML code in the section of your page:\r\n[code][/code][/li]\r\n[li]You are done![/li]\r\n[/ol]\r\n\r\nLinks found on your site will now sport a [b]tooltip[/b] and an [b]icon[/b]. The following pages are supported: achievement, profile, item, npc, object, spell, quest. Icons show up by default, you can customize the colors of your links, and easily rename them!\r\n\r\nYou can check out this [url=STATIC_URL/widgets/power/demo.html]working demo[/url], and see how easy it is!\r\n\r\n[h2]Related[/h2]\r\n\r\n[tabs name=Related]\r\n\r\n[tab name=\"Advanced usage\"]\r\n\r\nOnce you have the [/code]\r\n[/tab]\r\n\r\n[tab name=\"XML feeds\"]\r\n\r\n[h3]Items[/h3]\r\nAlso available are our item XML feeds. Every item in the database has a corresponding XML feed. You can reach those feeds either by ID or by name. For example:\r\n\r\n[ul]\r\n[li]By ID: HOST_URL?item=52021&xml[/li]\r\n[li]By name: HOST_URL?item=iceblade%20arrow&xml[/li]\r\n[/ul]\r\n\r\n[/tab]\r\n\r\n[tab name=\"Other resources\"]\r\n\r\nInterested in using our script in your forum? Check out [url=http://wowhead.com/forums&topic=3464]this thread[/url] for information on implementing it on many popular forum systems (phpBB, vBulletin, etc.) or check out the handy guides written by Wowheads users:\r\n\r\n[ul]\r\n[li][url=http://wowhead.com/forums&topic=3464#p37094]vBulletin[/url][/li]\r\n[li]phpBB: [url=http://wowhead.com/forums&topic=3464#p37492]2.x.x[/url] - [url=http://wowhead.com/forums&topic=3464.6#p58403]2.x.x Mod Version[/url] | [url=http://wowhead.com/forums&topic=14347&p=126922]3.0[/url] [small]by craCkpot[/small] - [url=http://wowhead.com/forums&topic=3464#p37204]3.0[/url] [small]by marcimi[/small] - [url=http://wowhead.com/forums&topic=3464.3#p42858]3.0 Mod Version[/url][/li]\r\n[li][url=http://wowhead.com/forums&topic=3464#p37618]Simple Machines Forum (SMF)[/url][/li]\r\n[li][url=http://wowhead.com/forums&topic=3464.3&p=4080#p40631]Invision Power Board (IPB)[/url][/li]\r\n[li][url=http://wowhead.com/forums&topic=3464.3&p=42952#p42952]WordPress Blog[/url] ([url=http://wowhead.com/forums&topic=3464.4#p43652]Plugin Version[/url])[/li]\r\n[li][url=http://wowhead.com/forums&topic=3464.7&p=63338#p61443]PHP Nuke-Evolution[/url][/li]\r\n[li][url=http://wowhead.com/forums&topic=3464.3#p43232]MyBB[/url][/li]\r\n[li][url=http://wowhead.com/forums&topic=3464.6#p48648]TikiWiki[/url][/li]\r\n[li][url=http://wowhead.com/forums&topic=3464.6#p49640]YaBB[/url][/li]\r\n[li][url=http://wowhead.com/forums&topic=3464.5#p46801]Drupal[/url][/li]\r\n[li][url=http://wowhead.com/forums&topic=3464.3#p42456]PunBB[/url][/li]\r\n[li][url=http://wowhead.com/forums&topic=10938]Dojo[/url][/li]\r\n[/ul]\r\n\r\n[/tab]\r\n\r\n[/tabs]'),(NULL,NULL,0,'searchbox',0,2,'[menu tab=2 path=2,16]\r\n\r\nThe code below will produce an iframe that contains the Aowow logo and a search box.\r\n\r\n[code]\r\n[/code]\r\n\r\n[h3]Parameters[/h3]\r\n\r\n[ul]\r\n[li][b]aowow_searchbox_format[/b] – String that specifies how big the iframe should be. The following values can be used:\r\n[pad]\r\n[table width=100%]\r\n[tr]\r\n[td width=20% align=center valign=top]\r\n\"160x200\"\r\n[img src=STATIC_URL/images/help/searchbox/preview-160x200.png]\r\n[/td]\r\n[td width=20% align=center valign=top]\r\n\"120x200\"\r\n[img src=STATIC_URL/images/help/searchbox/preview-120x200.png]\r\n[/td]\r\n[td width=20% align=center valign=top]\r\n\"160x120\"\r\n[img src=STATIC_URL/images/help/searchbox/preview-160x120.png]\r\n[/td]\r\n[td width=20% align=center valign=top]\r\n\"150x120\"\r\n[img src=STATIC_URL/images/help/searchbox/preview-150x120.png]\r\n[/td]\r\n[td width=20% align=center valign=top]\r\n\"120x120\"\r\n[img src=STATIC_URL/images/help/searchbox/preview-120x120.png]\r\n[/td]\r\n[/tr]\r\n[/table]\r\n[/li]\r\n[/ul]\r\n\r\n[h3]Tips[/h3]\r\n\r\n[ul]\r\n[li]You can style the iframe (e.g. adding a border) by using the following class name in your CSS code:\r\n[code].aowow-searchbox { ... }[/code][/li]\r\n[/ul]'),(NULL,NULL,0,'searchplugins',0,2,'[menu tab=2 path=2,8]\r\n\r\nSearch plugins make it easy to search the database right from your browser!\r\n\r\n[toc h3=false]\r\n\r\n[h2][img src=STATIC_URL/images/help/searchplugins/firefox.png border=0 margin=5 float=left]Firefox[/h2]\r\n\r\n[div float=right align=right][img border=2 src=STATIC_URL/images/help/searchplugins/os-firefox.png][/div]\r\n\r\n[script]\r\nfunction addPlugin()\r\n{\r\n if (typeof window.external.AddSearchProvider == \"function\")\r\n window.external.AddSearchProvider(\"STATIC_URL/download/searchplugins/aowow.xml\");\r\n else\r\n alert(\"This feature is unavailable.\");\r\n}\r\n[/script]\r\n[pad]\r\nEither\r\n[ul]\r\n[li]Click on the button below to install the search plugin in your browser or[/li]\r\n[li]Right-click your address bar and then clck on \"Add AoWoW\" or[/li]\r\n[li]Click on the [img src=STATIC_URL/images/icons/add.png border=0] on the browser search bar and then on [img src=STATIC_URL/images/icons/add.png border=0] \"Add search engine\"[/li]\r\n[/ul]\r\n\r\n[pad]\r\n[html]Install pluginInstall plugin[/html]\r\n[div clear=both][/div]\r\n\r\n[h2][img src=STATIC_URL/images/help/searchplugins/edge.png border=0 float=left][img src=STATIC_URL/images/help/searchplugins/chrome.png border=0 float=left]MS Edge / Google Chrome[/h2]\r\n\r\n[div float=right align=right][img border=2 src=STATIC_URL/images/help/searchplugins/os-edge.png][/div]\r\n[pad]\r\nFor Chrome-based browsers go to settings and fill in the add search engine form as shown.\r\n[pad]\r\n[div width=500px]\r\n[pre]HOST_URL/?search=%s[img src=STATIC_URL/images/icons/pages.gif float=right][/pre]\r\n[/div]\r\n[script]\r\nsetTimeout(() => $WH.clickToCopy($WH.qs(\"pre > img\"), \"HOST_URL/?search=%s\"), 100);\r\n[/script]\r\n[pad]\r\nSave your changes, and you\'ll be able to perform Aowow searches by typing \"db\" followed by the search terms in the address bar (e.g. db sword).\r\n[div clear=both][/div]\r\n'),(NULL,NULL,2,'page-not-found',0,2,'[tooltip name=AO815][b][color=q4]AO-815 Moteur Principal de Stabulation[/color][/b]\n[color=white]Lié lorsque utilisé\nUnique[/color]\n[color=q2]Utilise: Appelle le pouvoir de l\'Interwebs pour\ninvoquer l\'information demandé à Aowow.[/color]\n[color=q]\"En tout cas, c\'est ce que c\'est supposé faire...\"[/color][/tooltip]Quoi? Comment avez-vous... oubliez ça!\n\nIl semblerait que la page demandée n\'ait pas été trouvée. En tout cas, pas dans cette dimension.\n\nPeut-être que quelques réglages au [span class=tip tooltip=AO815][color=q4][u][AO-815 Moteur Principal de Stabulation][/u][/color][/span] pourraient résulter en l\'apparition soudaine de la page![pad][pad]\n\nOu vous pouvez essayer de [url=?aboutus#contact]nous contacter[/url] - la stabilité du AO-815 est discutable et vous ne voudriez pas un autre accident...\n\n[h2]Liens[/h2]\n[ul]\n[li]Retour à la [url=?]page d\'accueil[/url][/li]\n[li][url=?forums&board=1]Forum[/url] de feedback[/li]\n[/ul]'),(NULL,NULL,0,'faq',0,2,'[small]no questions have been asked yet[/small]\r\n\r\nbesides .. yes, i\'m insane.'),(NULL,NULL,0,'whats-new',0,2,'[small]this page for example[/small]'),(NULL,NULL,0,'aboutus',0,2,'[h3]This is [s]Sparta![/s] [u]Aowow[/u][/h3]\r\n\r\nA project for private servers to sensibly display the vast amount of data a private server contains.\r\n\r\nBuilt with TrinityCore in my neck, but i\'m trying to get away from that .. some time.\r\nWith it\'s own data structure it shouldn\'t be too hard to write a converter for MaNGOS, Ascent or whatever software you prefere.\r\n\r\nThe expected version is 3.3.5 (12340), everything else will get messy.'),(NULL,NULL,3,'page-not-found',0,2,'[tooltip name=AO815][b][color=q4]AO-815 Großkonfabulierungsmaschine[/color][/b]\n[color=white]Bei Benutzung gebunden\nEinzigartig[/color]\n[color=q2]Benutzen: Ersucht die Mächte der Internetze darum,\nAowow die benötigten Informationen zukommen zu lassen.[/color]\n[color=q]\"Das sollte es im Prinzip eigentlich tun...\"[/color][/tooltip]Was? Wie hast du... vergesst es!\n\nAnscheinend konnte die von Euch angeforderte Seite nicht gefunden werden. Wenigstens nicht in dieser Dimension.\n\nVielleicht lassen einige Justierungen an der [span class=tip tooltip=AO815][color=q4][u][AO-815 Großkonfabulierungsmaschine][/u][/color][/span] die Seite plötzlich wieder auftauchen![pad][pad]\n\nOder, Ihr könnt es auch [url=?aboutus#contact]uns melden[/url] - die Stabilität des AO-815 ist umstritten, und wir möchten gern noch so ein Problem vermeiden...\n\n[h2]Links[/h2]\n[ul]\n[li]Zur [url=?]Titelseite[/url] zurückkehren[/li]\n[li][url=?forums&board=1]Forum[/url] für Rückmeldungen[/li]\n[/ul]'),(NULL,NULL,6,'page-not-found',0,2,'[tooltip name=AO815][b][color=q4]Dispositivo de confabulación suprema AO-815[/color][/b]\n[color=white]Se liga al usar\nÚnico[/color]\n[color=q2]Uso: Clama a los poderes de Internet para\ninvocar información requerida a Aowow.[/color]\n[color=q]\"Al menos, eso es lo que se supone que hace...\"[/color][/tooltip]¿Pero qué? ¿Cómo? .... ¡olvídalo!\n\nParece que la página que buscas no pudo ser encontrada. Al menos, no en esta dimensión.\n\n¡Quizá un par de ajustes al [span class=tip tooltip=AO815][color=q4][u][Dispositivo de confabulación suprema AO-815][/u][/color][/span] puede que hagan que la página aparezca de repente![pad][pad]\n\nO, puedes intentar [url=?aboutus#contact]contactar con nosotros[/url] - la estabilidad del AO-815 es debatible y no queremos otro accidente...\n\n[h2]Enlaces[/h2]\n[ul]\n[li]Volver a la [url=?]página principal[/url].[/li]\n[li]Foro del [url=?forums&board=1]feedback[/url].[/li]\n[/ul]'),(NULL,NULL,0,'page-not-found',0,2,'[tooltip name=AO815][b][color=q4]AO-815 Major Confabulation Engine[/color][/b]\n[color=white]Binds when used\nUnique[/color]\n[color=q2]Use: Calls on the powers of the Interwebs to\nsummon requested information to Aowow.[/color]\n[color=q]\"At least, that\'s what it\'s supposed to do...\"[/color][/tooltip]What? How did you... nevermind that!\n\nIt appears that the page you have requested cannot be found. At least, not in this dimension.\n\nPerhaps a few tweaks to the [span class=tip tooltip=AO815][color=q4][u][AO-815 Major Confabulation Engine][/u][/color][/span] may result in the page suddenly making an appearance![pad][pad]\n\nOr, you can try [url=?aboutus#contact]contacting us[/url] - the stability of the AO-815 is debatable, and we wouldn\'t want another accident...\n\n[h2]Links[/h2]\n[ul]\n[li]Return to the [url=?]homepage[/url][/li]\n[li]Feedback [url=?forums&board=1]forum[/url][/li]\n[/ul]'),(NULL,NULL,0,'help=markup-guide',0,2,'[menu tab=2 path=2,13,7]Here we have quite a few nifty markup tags that users can insert into their comments and forum posts to improve the style and easily link to database entries! Many of these tags can easily inserted using the corresponding icon or dropdown menu found above the text box. We\'ve put together this quick reference for all of these handy tags for you guys so you can get on your way to making high quality posts and comments!\n\n[h2]Formatting Tags[/h2]\n[h3]Bold[/h3]\n\\[b]text[/b]\n\n[h3]Line break[/h3]\n\\[br] -> inserts a line break.\n\n[h3]Code[/h3]\n\\[code]text[/code] -> creates a block of text that ignores markup and uses a monospace font.\n\n[h3]Horizontal Rule[/h3]\n\\[hr] -> creates a horizontal rule\n\n[h3]Italics[/h3]\n\\[i]text[/i] -> [i]text[/i]\n\n[h3]Preformatted text[/h3]\n\\[pre]text[/pre] -> shows text with all whitespace preserved in a monospace font, but allows markup\n\n[h3]Strikethrough[/h3]\n\\[s]text[/s] -> [s]text[/s]\n\n[h3]Small text[/h3]\n\\[small]text[/small] -> [small]text[/small]\n\n[h3]Subscript[/h3]\n\\[sub]text[/sub] -> [sub]text[/sub]\n\n[h3]Superscript[/h3]\n\\[sup]text[/sup] -> [sup]text[/sup]\n\n[h3]Underline[/h3]\n\\[u]text[/u] -> [u]text[/u]\n\n[h2]Database Tags[/h2]\n\n\n[b]For all database tags:[/b]\nOptional attributes: site/domain (both work identically, only use one)\nValid options are: en (default), cn, de, es, fr, ru.\nThe purpose of these is to link to localized versions of items with the pretty db tags.\n[b]Example:[/b] \\[achievement=3579 domain=ru] -> [achievement=3579 domain=ru] \n\n[h3]Achievements[/h3]\n\\[achievement=3579] -> [achievement=3579]\n\n[h3]Classes[/h3]\n\\[class=11] -> [class=11]\n\n[h3]Events[/h3]\n\\[event=1] -> [event=1]\n\n[h3]Factions[/h3]\n\\[faction=749] -> [faction=749]\n\n[h3]Items[/h3]\n\\[item=12345] -> [item=12345]\n\nTo hide the icon: \\[item=12345 icon=false] -> [item=12345 icon=false]\n\n[h3]Itemsets[/h3]\n\\[itemset=699] -> [itemset=699]\n\n[h3]NPCs[/h3]\n\\[npc=32906] -> [npc=32906]\n\n[h3]Objects[/h3]\n\\[object=1733] -> [object=1733]\n\n[h3]Pets[/h3]\n\\[pet=45] -> [pet=45]\n\n[h3]Quests[/h3]\n\\[quest=7981] -> [quest=7981]\n\n[h3]Races[/h3]\n\\[race=11] -> [race=11]\n\n[b]To specify the gender of the icon:[/b] \\[race=11 gender=1] -> [race=11 gender=1] - 0 is male, 1 is female\n\n[h3]Skills[/h3]\n\\[skill=171] -> [skill=171]\n\n[h3]Spells[/h3]\n\\[spell=52398] -> [spell=52398]\n\\[spell=31565 buff=true] -> [spell=31565 buff=true]\n\n[h3]Statistics[/h3]\n\\[statistic=1076] -> [statistic=1076]\n\n[h3]Zones[/h3]\n\\[zone=3959] -> [zone=3959]\n\n[h2]HTML Tags[/h2]\n\n[h3]Anchor[/h3]\n\\[anchor=text] -> creates an anchor with the name \\\"text\\\" at this point.\n\n[h3]Ordered List[/h3]\n\\[ol]\\[li]list item[/li][/ol] -> [ol][li]list item[/li][/ol]\n\n[h3]Tables[/h3]\n[b]\\[table][/b]\nBorder: \\[table border=2]\nSpacing: \\[table cellspacing=2]\nPadding: \\[table cellpadding=2]\nWidth: \\[table width=500px] - Valid units are px, em, %\n\n[b]\\[tr][/b] - No attributes\n\n[b]\\[td][/b]\nAlign: \\[td align=right] - Valid options are left, right, center, justify\nVertical align: \\[td valign=baseline] - Valid options are top, middle, bottom, baseline\nColumn span: \\[td colspan=2]\nRow span: \\[td rowspan=2]\nWidth: \\[td width=500px] - Valid units are px, em, %\n\n[h3]Unordered List[/h3]\n\\[ul]\\[li]list item[/li][/ul] -> [ul][li]list item[/li][/ul]\n\n[h3]URLs[/h3]\n\\[url=http://www.wowhead.com]Wowhead[/url] -> [url=http://www.wowhead.com]Wowhead[/url]\n\\[url]http://www.wowhead.com[/url] -> [url]http://www.wowhead.com[/url]\n\\[url=http://www.google.com rel=item=12345]Rel link[/url] -> [url=http://www.google.com rel=item=12345]Rel link[/url]'),(8,589,0,NULL,0,2,'The [b]Wintersaber Trainers[/b] is an Alliance-only faction consisting of only two night elven NPCs that can both be found in [zone=618]. Currently, the only questgiver is [npc=10618], who is located at the top of Frostsaber Rock in Winterspring. Upon reaching exalted with this faction, Rivern will sell a special mount, the [item=13086].\n\nThis faction\'s mount is the only epic mount (100% riding speed) attainable in the game which only requires 75 riding skill (and thus only costs 90 Gold). The faction is noted for having no Horde counterpart and having the longest and most repetitive reputation grind of the entire game. The first quest can be attained at level 58, while the other two are attainable at level 60.\n\n[h3]Reputation[/h3]\nReputation with the Wintersaber Trainers can only be obtained through three repeatable quests. There are no faction items or mobs that reward repuation directly.\n\n[b]Neutral 0 to 1500[/b]\nOnly one repeatable quest will available at first, so until neutral 1500/3000 is reached the [quest=4970] quest should be repeated. Any Shardtooth and Chillvind mob in Winterspring will drop these. This quest should be done solo as the drop rates are low and not shared if others have the quest.\n\n[b]Neutral 1500 to Exalted[/b]\nHalfway through neutral the [quest=5201] quest will be available. This quest requires to kill 10 Winterfall mobs in the Winterfall Village, just east of Everlook. If the quest [quest=8464] has been done with the [faction=576], [item=21383] can drop from the Winterfall mobs. If a player wants both reputations, saving these until revered with Timbermaw Hold will result in a lot of \"free\" reputation.\n\nThis quest can be done in groups for increased speed. Players grinding either Wintersaber Trainers or Timbermaw Hold reputation can often be found in the Winterfall Village. Even with an epic mount, the travel to and from Winterfall Village takes up much time. There are tigers among the route who will daze you, which will result in a demount, this should be avoided (but can be hard as they\'ll catch up with you on a 60% mount). Usually this quest is repeated all the way to exalted, ignoring the third quest. \n\n[b]Honored to Exalted[/b]\nAt honored the third quest [quest=5981] is available. The quest requires the player to kill 8 Frostmaul giants. They are a lot harder than the Winterfall mobs and the travel lengths are quite longer. This quest is usually skipped, and instead Winterfall Intrusion is repeated.\n\nDue to some players grinding Timbermaw Hold reputation, in Winterfall Village among other places, this quest can indeed turn out to be a faster reputation reward than the Winterfall Intrusion one.'),(8,609,0,NULL,0,2,'The [b]Cenarion Circle[/b] is an organization of druids, both tauren and night elf, named after Cenarius. Its members are dedicated to protecting nature and restoring the damage done to it by malevolent forces.\n\nThe Circle has many posts, but their main home is the town of Nighthaven in the [zone=493]. Druids learn the spell [spell=18960] at level 10, but anyone else will have to make it to [zone=361] and find a way through the Timbermaw Furbolg tunnels.\n\nThe Circle\'s other major presence is in [zone=1377], where they combat the Silithid, the Qiraji, and Twilight\'s Hammer. Valor\'s Rest and Cenarion Hold serve as their bases in the hostile land, and offer many opportunities to adventurers seeking to aid the druids.\n\n[h3]Notable Members[/h3]\n[ul][li][npc=11832], son of Cenarius[/li][li][npc=3516], leader of the night elven druids[/li][li][npc=5769], leader of the tauren druids[/li][/ul]\n\n[h3]Reputation[/h3]\nThere are several ways to gain reputation with the Cenarion Circle. Aside from the available [url=?quests&filter=cr=1;crs=609;crv=0]quests[/url], you may do the following to gain reputation:[ul][li]Raid the [zone=3429]. This is by far the fastest way to gain reputation, as a full clear can net over 2000 reputation.[/li][li]Kill twilight cultists. These stop yielding reputation when you reach the end of friendly for [npc=11880] and [npc=11881], and at the end of honored for [npc=15201].[/li][li]Turn in [item=20404]. These drop off the cultists, and yield 250 reputation for 10 texts.[/li][li]Turn in [item=20513], [item=20514], and [item=20515]. These drop off the minibosses that are summoned at the windstones using the [itemset=492].[/li][li]Perform the [quest=8507]. These are either [url=?search=logistics+task+briefing]Logistics quests[/url], [url=?search=combat+task+briefing]Combat quests[/url], or [url=?search=tactical+task+briefing]Tactical quests[/url]. The badges you earn from these quests may then be turned in for additional reputation, if you chose to forsake the rewards.[/li][li]Collect [object=181598] from the zone and turn it in to your faction NPC.[/li][/ul]'),(8,729,0,NULL,0,2,'[b]Frostwolf Clan[/b], along with [npc=11946], lived along the [zone=36] practicing shamanism, and having Frost Wolves as their companions. The dwarven expedition known as the [faction=730] have started an expedition in the Frostwolf territory to excavate the valley and mine its veins, a transgression to the orcs who inhabited Alterac. This provoked a slaughter of the first expedition, and started the battle for [zone=2597].\n\n[h3]Reputation[/h3]\nPlayers can earn reputation in this faction by participating in the Alterac Valley battleground by doing various tasks as well as killing members of the opposite faction, the Stormpike Guard.\n\nYou are granted the player title [title=47] once exalted with the Frostwolf Clan and the other two battleground factions, [faction=889] and [faction=510].'),(8,730,0,NULL,0,2,'[b]Stormpike Guard[/b] is the Alliance faction in the [zone=2597] battleground. They are an expedition of dwarves of the Stormpike Clan, native to the \"valleys of Alterac\" in [zone=36]. The Stormpikes\' search for relics of their past and harvesting of resources in Alterac Valley have led to open war with the the orcs of the [faction=729] dwelling in the southern part of the valley. They were also issued with a \"sovereign imperialistic imperative\" by [npc=2784] to take the valleys of Alterac for [zone=1537]. \n\nThe main Stormpike base is Dun Baldar, where their leader, [npc=11948], resides with his marshals. His second in command, [npc=11949], is found south of Dun Baldar, at Stonehearth Outpost.\n\n[h3]Reputation[/h3]\nPlayers can earn reputation in this faction by participating in the Alterac Valley battleground by doing various tasks as well as killing members of the opposite faction, the Frostwolf Clan.\n\nYou are granted the player title [title=48] once exalted with Stormpike Guard and the other two battleground factions, [faction=890] and [faction=509].'),(8,749,0,NULL,0,2,'The [b]Hydraxian Waterlords[/b] are elementals that have made their home on the islands east of [zone=16]. Sworn enemies of the armies of [npc=11502]. Historically servants of the Old Gods, the four Elemental Lords served the gods with undying loyalty. The minions of Neptulon the Tidehunter were numerous and mindless. It is not yet known how [npc=13278] broke free of his lord\'s control (if indeed he has), or what is his ultimate goals are, but the Water elementals are the only elementals that do not attack the mortal races with abandonment.\n\nLocated on a remote island in the far east of Azshara, Duke Hydraxis offers some quests. The first two require killing various elementals in [zone=139] and [zone=1377]. Increased faction with the Waterlords opens up additional quests leading into the [zone=2717]. Any items obtained from the Hydraxian Waterlords, are obtained from its various quests.\n\nCompleting the questline allows players to obtain [item=17333] used to douse the runes found near most bosses in Molten Core. This is required to summon [npc=12018], the penultimate boss, and, after his defeat, to summon Ragnaros himself. Since there are seven runes, any raid needs at least seven players that bring a quintessence if they wish to finish the instance. Since most of the questline takes place within Molten Core, any raider can complete this task with little more than some traveling and an [zone=1583] run.\n\n[h3]Reputation[/h3]\nRepuation is gained through slaying the following elemental enemies of the waterlords.[ul][li][npc=11746] - 5 reputation, lasts until honored.[/li][li][npc=11744] - 5 reputation, lasts until honored.[/li][li][npc=7032] - 5 reputation, lasts until honored.[/li][li][npc=9017] - 15 reputation, lasts until revered.[/li][li][npc=14478] - 25 reputation, lasts until revered.[/li][li][npc=9816] - 50 reputation, lasts until revered.[/li][li][npc=11658], [npc=11673], [npc=12101] and [npc=11668] - 20 reputation, lasts until revered.[/li][li][npc=11659] and Lava Pack ([npc=12100], [npc=12076], [npc=11667], [npc=11666]) - 40 reputation, lasts until revered.[/li][li][npc=12118], [npc=11982], [npc=12259], [npc=12057], [npc=12056], [npc=12264], [npc=12098] - 100 reputation, lasts until exalted.[/li][li][npc=11988] - 150 reputation, lasts until the end of exalted.[/li][li][npc=11502] - 200 reputation, lasts until the end of exalted.[/li][/ul]Reaching revered status with the Hydraxian Waterlords allows players to obtain the [item=22754], which replenishes itself and thus eliminates the need to return to Hydraxis to obtain a new quintessence every week.'),(8,809,0,NULL,0,2,'The [b]Shen\'dralar[/b] are the faction of the Night Elves remaining in [zone=2557]. They are a group of high practitioners of arcane magic in order of their former Queen Azshara, and her followers, the Highborne. They have been living in Eldre\'Thalas (previous name of Dire Maul) since the Great Sundering. They are few, but their knowledge and mystic power are great, referring to things players think are powerful such as [b]Arcanums[/b] and [b]Librams[/b] as mere cantrips.\n\nTheir leader, [npc=11486], was in charge and oversaw the construction of the pylons to contain the great demon [npc=11496] and syphon his demonic power. After many long years though, it began to dwindle so he started killing the remaining night elves to maintain energy. So their spirits come to adventurers and ask them to kill him. There are very few of the original inhabitants left alive.\n\n[h3]Reputation[/h3]\nReputation can be gained by turning repeatedly in the three Librams of Dire Maul ([item=18333], [item=18334], [item=18332]). Turning in the following class books also gives some reputation:[ul][li][item=18357] - Warrior[/li][li][item=18363] - Shaman[/li][li][item=18356] - Rogue[/li][li][item=18360] - Warlock[/li][li][item=18362] - Priest[/li][li][item=18358] - Mage[/li][li][item=18364] - Druid[/li][li][item=18361] - Hunter[/li][li][item=18359] - Paladin[/li][li][item=18401] - Warrior & Paladin[/li][/ul]Both class books and librams give 500 Reputation points each.'),(8,889,0,NULL,0,2,'[b]Warsong Outriders[/b] is an orcish clan formerly led by [npc=18076], in which the clan was named after. The clan\'s Warsong Outriders form the Horde faction in the [zone=3277] battleground, where they are attempting to defend their logging operations in [zone=331] from the [faction=890].\n\nOne of the strongest and most violent clans, the Warsong Clan was also one of the most distinguished clans on Draenor and was able to evade Alliance expedition forces at every turn. Depicted as Grunts, they have mastered the use of swords and blades and a few of them have even attained the rank of a Blademaster.\n\n[h3]Reputation[/h3]\nReputation is gained through participation in the Warsong Gulch battleground. You gain 35 reputation each time your side captures a flag. This reputation gain is increased to 45 on holiday weekends.\n\nYou are granted the player title Conqueror once exalted with Warsong Outriders and the other two battleground factions, [faction=510] and [faction=729].'),(8,890,0,NULL,0,2,'[b]Silverwing Sentinels[/b] are the Alliance faction for the [zone=3277] battleground. The night elves, who have begun a massive push to retake the forests of [zone=331] are now focusing their attention on ridding their land of the [faction=889] once and for all. And so, the Silverwing Sentinels have answered the call and sworn that they will not rest until every last orc is defeated and cast out of Warsong Gulch.\n\n[h3]Reputation[/h3]\nReputation is gained through participation in the Warsong Gulch battleground. You gain 35 reputation each time your side captures a flag. This reputation gain is increased to 45 on holiday weekends.\n\nYou are granted the player title [title=48] once exalted with Silverwing Sentinels and the other two battleground factions, [faction=730] and [faction=509].'),(8,909,0,NULL,0,2,'The [b]Darkmoon Faire[/b] is a mysterious traveling carnival, which roams not only Azeroth but Outland as well. Led by the inimitable [npc=14823], a gnome of dubious heritage and unknown providence, the Faire brings fun, games, prizes, and exotic trinkets of unexpected power to [zone=215], [zone=12], or [zone=3519] each month.\n\nA variety of amusements can be had by the discerning fairegoer, but the most common attraction is the ticket redemption. A variety of merchants at the Faire collect items from around the worlds in exchange for [item=19182]. The tickets can, in turn, be saved up and turned in for prizes of varying worth and power. Several different ticket distributors are posted around the Faire, offering tickets for crafted items made by Leatherworkers, Blacksmiths, or Engineers as well as items gathered in the wild such as [item=11404] and [item=19933]. Tickets can be redeemed for many things, from flowers to hold in the off-hand to necklaces of great power.\n\nMany adventurers seek out the Darkmoon Faire to turn in the mystical [url=?items=15.0&filter=minle=1;cr=107;crs=0;crv=Combine+the+Ace]Darkmoon Cards[/url]. Darkmoon Cards come in eight suits, each of which has cards from Ace to Eight. Combining all cards in a suit produces a deck, which will start a quest to return that deck to the Darkmoon Faire. Each of the eight decks produces a different [url=?items=4.-4&filter=na=Darkmoon+Card]trinket[/url] with a different effect, some of which are quite powerful.\n\nThe Darkmoon Faire\'s usual schedule has it arriving on site on the first Friday of the month. For the weekend, the carnies will be seen setting up the midway, and the Faire will actually start early on the following Monday.'),(8,910,0,NULL,0,2,'The [b]Brood of Nozdormu[/b] is a faction consisting of the Bronze Dragonflight. Their leader [npc=15192] can be found outside the [b]Caverns of Time[/b], with many of its agents flying in the sky of [zone=1377].\n\nIn order to open the gates of [b]Ahn\'Qiraj[/b], one champion must complete a long quest line for the bronze dragon Anachronos. This reputation is also relevant in the [zone=3428]; to obtain epic quest gear and rings.\n\n[h3]Reputation[/h3]\nPlayers begin at 0/36000 hated, the lowest level of reputation possible.\n\nBrood of Nozdormu reputation can be earned through killing bosses in both Ahn\'Qiraj instances, killing monsters inside the Temple of Ahn\'Qiraj, and doing quests related to the dungeons. You can also farm [item=20384], though this will take a lot longer, and requires one to have obtained the [item=20383] in [zone=2677] for the [item=21175] quest chain.\n\nKilling trash in the Temple of Ahn\'Qiraj can only get you to 2999 / 3000 Neutral, at which point reputation can only be further advanced through quests and handing in [item=21229] and [item=21230]. You may want to save all the insignias until after you are Neutral, since at that point gaining reputation becomes much more difficult.'),(8,911,0,NULL,0,2,'[b]Silvermoon City[/b] is the capital of the blood elves, located in the northeastern part of the [zone=3430] within the kingdom of Quel\'Thalas. The breathtaking capital city of the blood elves may rival the dwarven capital of [zone=1537] as the world\'s oldest, still standing, capital. Recently rebuilt from the devastating blow dealt by the evil Prince Arthas, the city houses the largest population of blood elves left on Azeroth.[pad]Silvermoon today is only the eastern half of the original city; the western half was almost completely destroyed by the Scourge during the Third War. Falconwing Square, the second blood elf town, is the only part of western Silvermoon remaining in blood elf control. The Dead Scar (the path taken by Arthas Menethil and his undead army on the quest to resurrect Kel\'Thuzad, which carves through all of Eversong Woods) separates the rebuilt Silvermoon from the ruins of the western half. Interestingly, the Ruins of Silvermoon house no undead, instead they contain [url=?npcs&filter=na=wretched;maxle=8]Wretched[/url] and malfunctioning [npc=15638]. As it stands, what remains of Silvermoon City is still bigger than current Horde cities.\n\n[h3]History[/h3]\nThe city of Silvermoon was founded by the high elves after their arrival in Lordaeron thousands of years ago. The city was constructed out of white stone and living plants in the style of the ancient Kaldorei Empire. The city contained the famous Academies of Silvermoon as a center for the learning of Arcane Magic and Sunstrider Spire, a majestic palace home to the Royal family of the high elves. The Convocation of Silvermoon (also known as \"The Silvermoon Council\"), the ruling body of the high elves was also based here. Across a stretch of ocean to the north is the island that contains the Sunwell.[pad]Although Silvermoon itself was left relatively unscathed from the second war, in the third war the Death Knight Arthas led the Scourge into the city, attacking it on his quest to reach the Sunwell. The High Elven King was slain and the majority of the population killed. Scourge forces held the city for a time but abandoned it after the depleting of its resources.[pad]Though the city was attacked by the Scourge, it is not as destroyed as one might think. Though many of its plants are dead, and the occasional dead body is sprawled across the cobblestone, the city was immune to the fire and destruction. Silvermoon now resembles a ghost town, intact, but eerily abandoned. Nevertheless, treasure hunters often frequent Silvermoon to try and find some of the valuable artifacts that the elves left behind before they deserted the city, but the ghosts of Silvermoon\'s past inhabitants prevents anyone from taking anything.\n\n[h3]Reputation[/h3]\nA comprehensive list of quests that grant Silvermoon reputation can be found [url=?quests&filter=maxle=69;cr=1;crs=911;crv=0#00Mz]here[/url].[pad][npc=20612] is the quest giver for the repeatable [item=14047] quest that must be completed by non-blood elf Horde players in order to reach exalted and gain the ability to ride [url=?items=15.5&filter=na=hawkstrider]hawkstriders[/url], the mount of the blood elf race.'),(8,922,0,NULL,0,2,'[b]Tranquillien[/b] is a joint blood elf and Forsaken town and separate faction in the [zone=3433].\n\n[h3]History[/h3]\nAs the Scourge made their way to the Sunwell, the elves had no choice but to retreat. The town of Tranquillien was abandoned by the retreating elves. The town is now used by the blood elves and the Forsaken as their base of operation to launch attacks aiming to take back the Ghostlands from the Scourge. However, the city is surrounded by the Scourge and even couriers have trouble getting past the enemy to reach the town. The undead forces of Deatholme are the most dangerous threat to the town.\n\n[h3]Reputation[/h3]\nUnlike most starting areas, the town of Tranquillien is its own faction. All quests you do for them will garner at least 1000 reputation apiece. [npc=16528] acts as the Tranquillien quartermaster. Vredigar can be found near the inn and will sell various [span class=q2]uncommon[/span] items, and even a [span class=q3]rare[/span] cloak when you reach exalted! If you complete all of the Tranquillien quests, you should be exalted by approximately level 20.[pad]There are a variety of quests mostly concerning reclaiming overrun villages, investigating undead and helping around. The \"end\" of the quest-revealed lore surrounding Tranquillien culminates with the quest to kill [npc=16329].'),(8,930,0,NULL,0,2,'[b]Exodar[/b] is the faction associated with [zone=3557], the enchanted capital city of the draenei, built out of the largest husk of their crashed dimensional ship of the same name. It is located in the westernmost part of [zone=3524]. The Exodar faction leader is [npc=17468], who is located near the battlemasters in the Vault of Lights.\n\nThe history of the Exodar is a short one, as the draenei only recently raised it around the husk of their crashed ship, which is still smoking from the impact. The Exodar was once a naaru satellite structure around the dimensional fortress [url=?search=tempest+keep#z0z]Tempest Keep[/url]. The Exodar contains a large amount of technological wonders (due to its origins lying with the Tempest Keep) such as magically enchanted \"wires\" which transport holy energy throughout the ship to power the heating and lighting, as well as augmenting the draeneis\' already considerable powers.\n\n[h3]Reputation[/h3]\nAs with other major factions associated with the main races, Exodar reputation may be gained by doing repeatable cloth turn-in quests, killing the opposing faction in [zone=2597] (the blood elves), and doing the appropriately related quests. At honored, the player can purchase items from Exodar related vendors for 10% less, and at exalted, the player, if not a draenei, can purchase the [url=?items=15.5&filter=na=elekk;cr=93:92;crs=2:1;crv=0:0]various mounts[/url] sold by the Exodar. The cloth turn-in quests are available from [npc=20604] [small][/small].'),(8,932,0,NULL,0,2,'[b]The Aldor[/b] are an ancient order of draenei priests who revere the naaru, and to this day they assist the naaru known as [faction=935] in their battle against [npc=22917] and the Burning Legion. They are found primarily in [zone=3703] and [zone=3520]. Though they have suffered much at the hands of the blood elves who later became [faction=934], they have put aside open warfare for the sake of the Sha\'tar. The Aldor\'s most holy temple lies on the Aldor Rise, overlooking the city from the west.\n\nMost players will start at neutral with the Aldor. [npc=18166] in Shattrath City will give players an initial quest to become friendly with the Aldor or the Scryers. This choice is reversible if players feel the need. Draenei players will be friendly with the Aldor and hostile with the Scryers, whereas blood elf players will be hostile to the Aldor and friendly to the Scryers.\n\n[npc=19321] and [npc=20807] are located in the Aldor bank on the northern edge of the Terrace of Light. The Shrine of Unending Light on Aldor Rise is home to [npc=20616]Asuur [small][/small] and [npc=21906] [small][/small], who exchange epic armor tokens for [url=?itemsets&filter=ta=12]Tier 4[/url] and [url=?itemsets&filter=ta=13]Tier 5[/url] gear, respectively.\n\n[i]Note: Reputation gains with Aldor correspond with a 10% greater loss of reputation with the Scryers. Most reputation gains with the Aldor will also grant 50% of the reputation gained toward your standing with the Sha\'tar.[/i]\n\n[h3]Reputation[/h3]\n[b]Until Honored[/b]\nPlayers looking to gain the higher reputation ranks (revered, exalted) may wish to save non-repeatable quests until after reaching honored.\n\nTurning in 10 [span class=q1][item=29425][/span] to [npc=18537] in Aldor Rise will grant 250 reputation with Aldor. There is also a repeatable quest for single mark turn-ins which yields 25 rep. These marks drop from low ranking Burning Legion members found in most zones in Outland, including the two camps north of Auchindoun in the Bone Wastes of [zone=3519]. Approximately 240 marks are required to go from friendly to honored. In addition these quests provide Sha\'tar reputation; 125 reputation per 10 or 12.5 reputation per single turn in.\n\nPlayers who also desire [faction=978] or [faction=941] reputation may prefer killing orcs at Kil\'Sorrow Fortress in southeastern [zone=3518], as they yield marks as well as 10 Kurenai or Mag\'har reputation per kill.[pad][b]Until Exalted[/b]\nOnce you reach level 68 you may also turn in [span class=q1][item=30809][/span] at the same rates as Marks of Kil\'jaeden. These drop from high-ranking followers of the Burning Legion. If you wish, you may turn in the higher level marks before honored reputation. In [zone=3522], grinding in Death\'s Door is the most compact group of mobs that drop marks.[pad][b]Fel Armaments[/b]\n[span class=q2][item=29740][/span] may be turned in at any time to [npc=18538]Ishanah [small][/small] inside the Shrine of Unending Light on the Aldor Rise. This will increase your reputation with Aldor by 350 per hand-in. In addition to reputation gains, you will receive [span class=q1][item=29735][/span], which is currency for the purchase of shoulder enchants from Inscriber Saalyn in the Aldor bank.\n\n[h3]Switching to Aldor[/h3]\nTo change your faction from the Scryers to the Aldor to access their crafting recipes (and undo all reputation progress you have made), find [npc=18597], an Aldor in Lower City. She offers a repeatable quest for 8x [span class=q1][item=25802][/span]. Once you are neutral with the Aldor, you may no longer receive this quest.'),(8,933,0,NULL,0,2,'Led by [npc=19674], [b]The Consortium[/b] are ethereal smugglers, traders and thieves that have come to Outland. Their main base of operations and biggest settlement is the Stormspire, but they can be found at Midrealm Post, the Aeris Landing, within the [zone=3792] of Auchindoun and various other places.\n\nUpon reaching Friendly status, players are officially considered members of the Consortium and given a salary. The salary is a bag of gems at the beginning of every month, given by [npc=18265] at Aeris Landing. Higher reputation with the Consortium yields higher qualities and quantities of jewels each month.\n\n[h3]Reputation[/h3]\n[b]Until Friendly[/b][ul][li]Run Mana-Tombs in [i]normal[/i] mode, ~1200 reputation per run.[/li][li]Turn in [item=25416] at [npc=18265].[/li][li]Turn in [item=25463] at [npc=18333].[/li][/ul][b]Friendly to Honored[/b][ul][li]Run Mana-Tombs in [i]normal[/i] mode, ~1200 reputation per run.[/li][li]Turn in [item=25433] at [npc=18265].[/li][li]Turn in [item=29209] at [npc=19880].[/li][/ul][b]Honored to Exalted[/b][ul][li]Run Mana-Tombs in [i]heroic[/i] mode, ~2400 reputation per run.[/li][li]Complete all available [url=?quests&filter=cr=1;crs=933;crv=0]quests[/url].[/li][li]Turn in [item=25433] at [npc=18265].[/li][li]Turn in [item=29209] at [npc=19880].[/li][/ul]Characters trying to simultaneously earn reputation with the [faction=941] or [faction=978] and the Consortium may want to focus on killing ogres ([url=?npcs&filter=na=boulderfist;cr=6;crs=3518;crv=0]Boulderfist[/url], [url=?npcs&filter=na=Warmaul;cr=6;crs=3518;crv=0]Warmaul[/url]) in Nagrand and saving the Obsidian Warbeads for Consortium turn-ins. The only caveat is the drop rate, which is roughly 33% for the warbeads, while it is 50% on the insignias. If you are level 70 and want a faster grind without concern for Mag\'har/Kurenai reputation, then you may want to grind insignias instead. Then again, the ogres are generally easier to grind, ranging from level 65 to 67. The choice is ultimately up to the player.'),(8,934,0,NULL,0,2,'[b]The Scryers[/b] are blood elves who reside in [zone=3703] led by [npc=18530]. The group broke away from [npc=19622] and offered to assist the Naaru at Shattrath City. They are at odds with the [faction=932], and compete with them for power within Shattrath and the Naaru\'s favor.[pad]Most players will start at neutral with the Aldor. [npc=18166] in Shattrath City will give players the choice of aligning themselves with the Scryers or Aldor after completing [quest=10211]. This choice is reversible if players feel the need. Blood elf players will be friendly with the Scryers and hostile with the Aldor, whereas draenei players will be hostile to the Scryers and friendly to the Aldor.[pad]The Scryers have both a [npc=19251] trainer and a [npc=19252] trainer. Due to this, the enchanter nestled deep within [zone=1337] is rendered obsolete.[pad][npc=19331] and [npc=20808] are located in the Scryers bank on the southern edge of the Terrace of Light. The Seer\'s Library in the Scryer\'s Tier is home to [npc=20613] [small][/small] and [npc=21905] [small][/small], who exchange epic armor tokens for [url=?itemsets&filter=ta=12]Tier 4[/url] and [url=?itemsets&filter=ta=13]Tier 5[/url] gear, respectively.[pad][i]Note: Reputation gains with Scryers correspond with a 10% greater loss of reputation with the Aldor. Most reputation gains with the Scryers will also grant 50% of the reputation gained toward your standing with the [faction=935].[/i]\n\n[h3]Lore[/h3]\nAfter enduring relentless assaults, the harried Sha\'tar and Aldor guards braced for the next wave as it marched over the horizon. This time, the attack came from the armies of [npc=22917]. A large regiment of blood elves had been sent by Illidan’s ally, Prince Kael\'thas Sunstrider, to lay waste to the city. As the regiment of blood elves crossed the bridge, the Aldor’s exarches and vindicators lined up to defend the Terrace of Light. Then the unexpected happened, the blood elves laid down their weapons in front of the city\'s defenders. Their leader, a blood elf elder known as Voren’thal, stormed into the Terrace of Light and demanded to speak to the naaru [npc=18481]. As the naaru approached him, Voren’thal knelt and uttered the following words: \"I’ve seen you in a vision, naaru. My race’s only hope for survival lies with you. My followers and I are here to serve you.\"[pad]The defection of Voren’thal and his followers was the largest loss ever incurred by Kael’thas’ forces. Many of the strongest and brightest amongst Kael’thas’ scholars and magisters had been swayed by Voren’thal\'s influence. The naaru accepted the defectors who became known as the Scryers.\n\n[h3]Reputation[/h3]\n[b]Until Honored[/b]\nPlayers looking to gain the higher reputation ranks (revered, exalted) may wish to save non-repeatable quests until after reaching honored.[pad]Turning in 10 [span class=q1][item=29426][/span] to [npc=18531] in Scryer\'s Tier will grant 250 reputation with the Scryers. These signets can also be turned in one at a time at the same exchange rate, 25 reputation per signet. These signets drop from low ranking Firewing members found in the northeast section of Terrokar Forest. This repeatable quest becomes unavailable at honored. If no other reputation quests are done, 240 signets are required to go from friendly to honored.[pad][b]Until Exalted[/b]\nOnce you reach level 68, you may also turn in [span class=q1][item=30810][/span]. These drop from high-ranking Sunfury blood elves (found in [zone=3523], [zone=3520], and the [url=?search=tempest+keep+-eye+-kael]Tempest Keep[/url] instances). If you wish, you may turn in the higher level signets before honored reputation, however it is recommended that you save them for after you hit honored. For every 10 signets, you will gain 250 reputation. Once you hit honored it will take approximately 1,320 Sunfury signets to go from honored to exalted if no other reputation is earned.[pad][b]Arcane Tomes[/b]\n[span class=q2][item=29739][/span] may be turned in at any time to Voren\'thal the Seer inside the The Seer\'s Library on the Scryer\'s Tier. This will increase your reputation with the Scryers by 350 per hand-in. If you wish, you may turn in the Arcane Tomes before honored reputation, however it is recommended that you save them for after you hit honored. Once you hit honored it will take approximately 94 Arcane Tomes to go from honored to exalted if no other reputation is earned. In addition to reputation gains, you will receive an [span class=q1][item=29736][/span], which is currency for the purchase of shoulder enchants from Inscriber Veredis, who resides in the Scryers bank.\n\n[h3]Switching to Scryers[/h3]\nTo change your faction from Aldor to Scryers to access their crafting recipes (and undo all reputation progress you have made), find [npc=18596], a Scryers in the Lower City. She offers you a repeatable quest, [quest=10024], that requires you to find eight [span class=q1][item=25744][/span]. Once you are Neutral with the Scryers, you can no longer receive this quest. The quest gives you +250 Scryers reputation and -275 Aldor reputation (in addition, the quest also gives you +125 reputation with The Sha\'tar).'),(8,935,0,NULL,0,2,'[b]The Sha\'tar[/b], or \"born of light,\" are naaru that aided [faction=932], the order of draenei priests formerly led by [npc=17468], in rebuilding [zone=3703]. The city was destroyed by the Orcs during their rampage across Draenor prior to the First War. Defeat of the Burning Legion is the Sha\'tar\'s ultimate goal; the Sha\'tar are aided in this war by the Aldor and their rivals, the blood elf faction known as [faction=934]. The Aldor and the Scryers fight for the favor of the Sha\'tar so that they may be assisted in their war by the naaru\'s powers. The entity that leads the Sha\'tar is known as [npc=18481]; he can be found upon the Terrace of Light in Shattrath City.\n\nBoth Alliance and Horde players begin as Neutral toward the Sha\'tar. Players can increase their Sha\'tar reputation through various quests, by raising their reputation with the Aldor or Scryers, or by adventuring into [url=?search=Tempest+Keep#z0z]Tempest Keep[/url].\n\n[h3]Reputation[/h3]\n[b]Until Honored[/b]\nReputation can be gained from Scryer/Aldor signet/mark turn-ins. The following will only grant Sha\'tar reputation until you achieve Honored status: [item=29426], [item=30810], and [item=29739] for the Scryers; [item=29425], [item=30809], and [item=29740] for the Aldor. In addition, these will require more turn-ins to produce equable Sha\'tar reputation to the main faction. Note that this reputation gain does not show up in the combat log, but can be verified by looking at your reputation panel.\n\nReputation can also be gained by running Tempest Keep: [zone=3847], [zone=3846] and [zone=3849].\n\n[b]Through Exalted[/b]\nAfter exhausting the reputation rewards from Aldor/Scryer turn-ins and Mechanar runs, players may wish to complete the few Sha\'tar quests available. In addition to the quests, instance runs in Tempest Keep: Botanica, Arcatraz and Mechanar will continue to grant reputation. At this point, it is probably more worthwhile to run these instances in Heroic mode.'),(8,941,0,NULL,0,2,'The [b]Mag\'har[/b] are a faction of brown-skinned orcs who remain on Outland and have separated themselves from the other remaining orc clans that fell prey to [npc=17257] and joined his army of fel orcs (that are now led by the powerful [npc=16808]). The Mag\'har are settled in the stronghold of Garadar in the beautiful land of [zone=3518], once home to the majority of the orcs along with [zone=3519] and the [zone=3522].[pad]The Mag\'har orcs have never been corrupted by Mannoroth or Magtheridon and thus remained untouched by the bloodlust. Unlike their former clanmates who live in the ruins of their once-mighty holds, the Mag\'har are made up of members of different orc clans who escaped corruption. The current leader of the Mag\'har, venerable [npc=18141], is an old and wise orc, yet she has recently fallen extremely ill. [npc=18063], son of the mighty Grom Hellscream, serves as the Mag\'har\'s military chief, aided by [npc=18106], son of the venerable chieftain of the Bleeding Hollow clan, Kilrogg Deadeye. In addition, there is an NPC within a Mag\'har camp to the west known as [npc=18229].[pad]It is not clear how the Mag\'har managed to retain their original brown skin. Orcish skin turns green when exposed to warlock magic, regardless of the individual\'s beliefs or practices; Garrosh and Jorin would certainly have been exposed, given the positions of their fathers. \n\nHorde players start out at unfriendly with the Mag\'har. Alliance players will always be treated as hostile. The Alliance counterpart to this faction are the [faction=978].\n\n[h3]Questing[/h3]\nQuests for the Mag\'har begin in [zone=3483] with [quest=9400] from [faction=947]. This quest will lead you to a small Mag\'har outpost north of Hellfire Citadel. Once in Nagrand, players will find the main Mag\'har city, Garadar. The city holds most of the remaining quests that will reward Mag\'har reputation.\n\nNote: You MUST have completed the quest chain of \"The Assassin\" up until the quest [quest=9410] (where you become Neutral) in order for you to talk to most people in Garadar.\n\n[h3]Reputation[/h3]\nReputation can be gained from killing [url=?npcs&filter=na=kil%27sorrow;ra=-1;rh=-1]Kil\'sorrow cult members[/url], [url=?npcs&filter=na=Murkblood;ra=-1;rh=-1;cr=6;crs=3518;crv=0]Murkblood Broken[/url], [url=?npcs&filter=na=warmaul+-marker]Warmaul[/url] and [url=?npcs&filter=na=boulderfist;minle=64;ra=-1;rh=-1]Boulderfist[/url] ogres in Nagrand. Players may also turn in 10x [item=25433], which drop from these ogres.[pad]Players seeking [faction=933] reputation may wish to save their warbeads, as Mag\'har reputation is generally easier to obtain.[pad]Players seeking [faction=932] reputation may prefer killing cult members at Kil\'Sorrow Fortress, as they drop [item=29425] for Aldor reputation turn-ins.\n\n[i]Note: These monsters and quests do not have a limit, they grant reputation all the way through exalted![/i]'),(8,942,0,NULL,0,2,'Upon the reopening of the Dark Portal to Outland, the [faction=609] dispatched an exploratory force, known as the [b]Cenarion Expedition[/b], to explore the uncharted world. Much like the Circle, it is a coalition of night elf and tauren forces. Since the opening of the Dark Portal, the Cenarion Expedition has quickly gained in size and autonomy, achieving enough power to be considered its own faction. The Expedition maintains its primary base at Cenarion Refuge in [zone=3521]; it has also made its presence known on [zone=3483], in [zone=3519], and in the [zone=3522]. Cenarion Refuge is located immediately west of Thornfang Hill.\n\nThe Refuge is located in the Zangarmarsh for the primary reason of studying the rich wildlife located there. However, the Expedition has discovered troubling goings-on in the marsh. Water levels in many parts of Zangarmarsh are decreasing, and some areas such as the Dead Mire have already suffered greatly from this strange phenomenon. It has become known that this decrease in the water levels can be attributed to pumps that have been constructed in the Marsh by the naga. Their purpose is to create a new Well of Eternity for [npc=22917]. However, the Expedition cannot afford direct confrontation with the naga so numerous in the Zangarmarsh and [url=?search=coilfang#c0z]Coilfang Reservoir[/url]. It needs the aid of those willing to assist the druids in their dangerous battle against those who seek to disturb the marsh\'s natural balance. Quite naturally, those heroic enough to fight the naga at Coilfang Reservoir will be well rewarded.\n\n[h3]Reputation[/h3]\n[b]Neutral to Honored[/b]\nKill Naga, while also running [zone=3717] whenever you can; a good instance run will net reputation faster than soloing. Alternatively, the player can begin turning in [item=24401] for a chance at an [item=24407], which can be turned in for 500 reputation. It is suggested that the player save his Uncatalogued Species until after Honored status is achieved, as the quest cannot be continued past that point, while Uncatalogued Species can be used until Exalted.\n\nIf you are an herbalist, and interested in [faction=970] reputation, you may want to grind the [url=?npcs&filter=na=Bog+Lord]Bog Lords[/url] which can be found in the NE, SE, and SW corners of Zangarmarsh. Their bodies can be \"picked\" by herbalists and often yield Unidentified Plant Parts, while every kill yields 15 reputation with Sporeggar.[pad][b]Honored to Revered[/b]\nOnce the player is Honored, running Slave Pens and the [zone=3716] (with the exception of [npc=17770] and some giants), will no longer grant reputation. You should now do any Cenarion Expedition quests in Hellfire Peninsula, Zangarmarsh, Terokkar Forest and the Blade\'s Edge Mountains. It is also the time to turn in any Uncatalogued Species you have found. Doing this should get you part of the way into Revered.\n\nAlternatively, you can finish leveling to 70 and run [zone=3715]. Each run gives just over 1500 reputation if you clear all mobs. Also within the Steamvault lies a repeatable quest, [quest=9764], which begins with [item=24367]. You will then be able to turn in [item=24368], which drop in both Steamvault and Slave Pens, receiving 250 reputation for the first turn-in and 75 reputation each thereafter. This turn-in is available all the way to Exalted.\n\nOnce you are 70 and have upgraded your gear, you can opt to run Slave Pens, Underbog, and Steamvault on Heroic Mode upon purchasing the [item=30623]. While the instances are difficult, they award significant reputation: regular mobs are worth 15 reputation, 2 for non-elites, and 150/250 for bosses. This method works until Exalted.[pad][b]Revered to Exalted[/b]\nContinue with the same strategy as above: finish any remaining quests, run Steamvault, and continue with [item=24368] turn-ins.\n\nIt is also possible to run Slave Pens, Underbog, and Steamvault on Heroic Mode. The reputation gained is not much more than running Steamvault in normal mode, whilst the time investment for heroic dungeons is much higher, possibly resulting in a lower net reputation per hour, however the loot is better and you will receive [item=29434] from the bosses which can be used to purchase high quality epic gear.'),(8,946,0,NULL,0,2,'A refuge of human, elven, draenei and dwarven explorers, [b]Honor Hold[/b] is the first major town Alliance explorers will encounter while traversing Outland. Vestiges of the Sons of Lothar, veterans of the Alliance that first came into Draenor, have steadfastly held on to this Hellfire outpost. They are now joined by the armies from Stormwind and Ironforge.\n\n[h3]Reputation[/h3]\nHonor Hold reputation is gained through various means in Hellfire Peninsula. Mobs in and around Hellfire Citadel reward Honor Hold reputation, as well as quests picked up in town. Due to the lack of representatives in other areas, there is a large gap between Honored and Exalted during which you may not attain any Honor Hold reputation from questing and killing mobs in Outland once you depart Hellfire Peninsula.\n\n[b]Through friendly[/b]\nMobs in [zone=3562] and [zone=3713] will award reputation through Friendly. One option is to grind reputation via Ramparts and Blood Furnace runs until honored before doing any Honor Hold quests outside the instances, as those continue to yield reputation up to Exalted. You may also want to check out the following outdoor mobs which give reputation if you are Neutral. These mobs will not give reputation once you are Friendly with Honor Hold.[ul][li][npc=19415] [/li][li][npc=16878] [/li][li][npc=16870][/li][li][npc=16867][/li][li][npc=19414] [/li][li][npc=19413] [/li][li][npc=19411] [/li][li][npc=19422][/li][/ul]To make the best use of available resources, you may want to grind reputation with Honor Hold through Hellfire Ramparts and Blood Furnace prior to completing any Honor Hold quests. \n\n[b]PvP[/b]\nPlayers that enjoy PvP can earn Honor Hold reputation through the daily quest [quest=10106]. This quest awards 70 silver and 150 Honor Hold reputation, but can only be completed once a day and counts towards your 25 daily quest limit. Completion of this quest also yields three [span class=q1][item=24579][/span], which are used as currency for various types of items and gear when turned into [npc=17657] and [npc=18266] in Honor Hold as well as the [npc=18581] in Zangarmarsh.\n\n[i]Tip: You can use these marks to purchase [span class=q1][item=24520][/span] from Warrant Officer Tracy Proudwell and increase the amount of reputation (and experience) gained while running these instances.[/i]\n\n[b]Through Exalted[/b]\nFrom here on out there are only two ways to achieve Revered and Exalted status:[ul][li][zone=3714], this instance requires level 68 and the [span class=q1][item=28395][/span] (only one party member needs the key). Mobs in Shattered Halls will yield reputation through Exalted.[/li][li]After achieving Honored status you can purchase the [span class=q1][item=30622][/span] which grants access to the heroic mode of all Hellfire Citadel instances. Mobs in all Heroic mode Hellfire Citadel instances will yield slightly more reputation than those found in non-heroic Shattered Halls, and will continue to yield reputation through Exalted.[/li][/ul]'),(8,947,0,NULL,0,2,'The expedition sent through the Dark Portal by Thrall has built a stronghold in Hellfire Peninsula. [b]Thrallmar[/b] serves as a base of operations for much of the Horde\'s activities in Outland.\n\n[h3]Reputation[/h3]\nReputation for Thrallmar up to Honored is relatively easy to earn. Even the easiest quests (those that take you from one quest giver to the next up the road, for example) can yield 75 reputation points, while those that require some effort to complete typically yield 250 reputation points or more. Some group quests that involve killing an elite can yield as much as 1000 reputation points.\n\nIf you do the bulk of the Thrallmar quests instead of quickly moving on to the next zone, you might expect to reach Honored after 1 or 2 levels of play. However, once you reach Honored, you hit an earnings barrier that you can only remove when you are level 68 and can start re-earning points in the [zone=3714] dungeon.\n\n[b]Neutral through Friendly[/b]\nReputation from mobs in [zone=3562] and [zone=3713] stops at 5999/6000 friendly. One option is to grind reputation via Ramparts and Blood Furnace runs to 5999/6000 before doing any Thrallmar quests outside the instances, as those continue to yield reputation up to Exalted.\n\nAlso, the level 63 mobs outside Hellfire Citadel (on the path) give you 5 reputation each.\n\n[b]Friendly through Honored[/b]\nPlayers that enjoy PvP can earn Thrallmar reputation through the daily quest [quest=10110]. This quest awards 70 silver and 150 Thrallmar reputation, but can only be completed once a day and counts towards your 25 daily quest limit. Completion of this quest also yields three [item=24581], which are used as currency for various types of items and gear when turned into [npc=18267] and the [npc=18564] in Thrallmar and near Zabra\'jin in [zone=3521] respectively.\n\nBlood Furnace and Ramparts instance runs will be your best bet for this reputation bracket. Be aware though, that they will only take you to the end of Honored. You will need to run Shattered Halls to reach Revered status.\n\n[b]Revered to Exalted[/b]\nFrom this point on, gaining reputation through Exalted requires one of two things:[ul][li]Access to Shattered Halls, one of the wings of Hellfire Citadel, which requires level 68 and either the [span class=q1][item=28395][/span] or a rogue with 350 lockpicking skill.[/li][li]Doing Heroic versions of Hellfire Citadel dungeons, which typically require you to be well geared and level 70.[/li][/ul]Both of these give reputation until you reach Exalted status. A full clear of Shattered Halls nets you about 2000 reputation points, trash mobs generally yield 6 or 12 each, with up to 150 points from bosses. Heroic trash yields 15-25 points, with bosses worth more. \n\n[i]Tip: You can purchase [span class=q1][item=24522][/span] from Battlecryer Blackeye for use during instance runs to speed up the reputation (and experience) gaining process![/i]'),(8,967,0,NULL,0,2,'[b]The Violet Eye[/b] is a secret sect founded by the Kirin Tor of Dalaran to spy on the Guardian of Tirisfal, [npc=15608], in his tower of [zone=2562]. Though Medivh is dead, the Violet Eye remains in Karazhan, defending against the evil that appears to have taken hold in the absence of its master. \n\nIt is unknown whether Medivh\'s apprentice, [npc=18166], was a member of the Violet Eye, or whether he knew of their activities at the time (though he does seem to be aware of them now).\n\n[h3]Reputation[/h3]\nViolet Eye reputation is gained by killing mobs inside Karazhan and completing Karazhan related quests. Reputation from Karazhan mobs can be gained from neutral standing all the way to exalted. Each trash mob awards around 15 reputation, with the bosses award more.\n\n[npc=18253] begins a fairly long quest chain starting with [quest=9824] and [quest=9825]. This quest line rewards players with [span class=q1][item=24490][/span] and culminates with [quest=9644]. Full completion of this quest line rewards approximately 10,270 reputation.\n\n[h3]Reputation Rewards[/h3]\n[npc=18253] will offer players rings as rewards for reputation level gains in the form of quests. The first such quest is available at neutral standing and may be completed at friendly. You will receive a new and upgraded version of the ring you chose each time you break into a new reputation tier. The rings are sorted into the following 4 categories:[ul][li][quest=10731]: [item=29280], [item=29281], [item=29282] and [item=29283][/li][li][quest=10729]: [item=29284], [item=29285], [item=29286] and [item=29287][/li][li][quest=10732]: [item=29276], [item=29277], [item=29278], and [item=29279][/li][li][quest=10730]: [item=29288], [item=29289], [item=29291] and [item=29290][/li][/ul][npc=16388], a blacksmith located inside Karazhan just after [npc=15550], offers players with high enough reputation the ability to buy epic blacksmithing plans. Players who are honored or above will also be able to repair armor and weapons at this vendor.\n\n[npc=18255], who stands just outside the main gates of Karazhan, will sell an epic jewelcrafting recipe and shoulder enchant to players who have an honored or above standing with The Violet Eye.'),(8,970,0,NULL,0,2,'The sporelings are a mostly peaceful race of mushroom-men native to Outland. Their home, [b]Sporeggar[/b], is located in the western bogs of [zone=3521].\n\n[h3]Reputation[/h3]\nPlayers both Alliance and Horde start out unfriendly with Sporeggar. There are many ways to increase your reputation at the beginning:[ul][li]Bringing 10 [span class=q1][item=24290][/span] to [npc=17923] to complete [quest=9739][/li][li]Bringing 6 [span class=q1][item=24291][/span] to Fahssn to complete [quest=9743] [i](both of these quests will be available only if you are below friendly)[/i][/li][li]Killing [url=?search=bog+lord+-hungry#z0z]Bog Lords[/url] [i](lasts until the end of honored)[/i][/li][li]Killing [npc=18137] and [npc=18136] [i](lasts until the end of revered)[/i][/li][li]Bringing 10 [span class=q1][item=24245][/span] to [npc=17924] in Sporeggar [i](lasts only during neutral)[/i][/li][/ul]After you hit [b]friendly[/b], a new handful of repeatable quests opens up at the same time Fahssn\'s quests and the Glowcap turnins become unavailable, these include:[ul][li]Killing 12 each of [npc=18088] and [npc=18089] for [npc=17856] to complete [quest=9726][/li][li]Bringing 10 [span class=q1][item=24449][/span] to [npc=17925] to complete [quest=9806][/li][li]Venturing into [zone=3716] to gather 5 [span class=q1][item=24246][/span] for Gzhun\'tt to complete [quest=9715][/li][/ul]These 3 quests are repeatable and will be available to the end of exalted.\n\nPlayers who are exalted with Sporeggar should speak to [npc=17877] for one final quest.'),(8,978,0,NULL,0,2,'Draenei for \"redeemed.\" These Broken have escaped the grasp of their various slavers in Outland and have made their home at Telaar in southern [zone=3518]. It is there that they seek to rediscover their destiny. They also maintain a small presence at Orebor Harborage, [zone=3521]. Their quartermaster, [npc=20240], is located outside the inn in Telaar, below the flight point.\n\nAlliance players start out at unfriendly with the Kurenai. Horde players will always be treated as hostile. The Horde counterpart to this faction are [faction=941].\n\n[i]Kurenai is Japanese for \"crimson\".[/i]\n\n[h3]Gaining Reputation[/h3]\nReputation can be gained from killing [url=?npcs&filter=na=kil%27sorrow;ra=-1;rh=-1]Kil\'sorrow cult members[/url], [url=?npcs&filter=na=Murkblood;ra=-1;rh=-1;cr=6;crs=3518;crv=0]Murkblood Broken[/url], [url=?npcs&filter=na=warmaul+-marker]Warmaul[/url] and [url=?npcs&filter=na=boulderfist;minle=64;ra=-1;rh=-1]Boulderfist[/url] ogres in Nagrand. Players may also turn in [item=25433] (10), which drop from these ogres.\n\nPlayers seeking [faction=933] reputation may wish to save their warbeads, as Kurenai reputation is generally easier to obtain.\n\nPlayers seeking [faction=932] reputation may prefer killing cult members at Kil\'Sorrow Fortress, as they drop [item=29425] for Aldor reputation turn-ins.\n\n[i]Note: These monsters and quests do not have a limit, they grant reputation all the way through exalted![/i]'),(8,989,0,NULL,0,2,'The [b]Keepers of Time[/b] are bronze dragons hand-picked by Nozdormu to watch over the Caverns of Time. They are led by [npc=19932] and [npc=19933], who are also acting leaders of the Bronze Dragonflight in Nozdormu\'s absence.\n\n[h3]Reputation[/h3]\nCurrently the only way to gain the favor of the enigmatic bronze dragons is through [zone=2367] and [zone=2366] instance runs. Keepers of Time reputation rewards may be found at the Keepers\' quartermaster, [npc=21643]. The Keepers will require you to be level 66 and complete the short quest [quest=10277] before allowing passage into Old Hillsbrad Foothills to fulfill [npc=17876]\'s destiny to become the Warchief of the Horde.'),(8,990,0,NULL,0,2,'The [b]Scale of the Sands[/b] is a secretive subgroup of the Bronze Dragonflight, led by [npc=19935], prime mate of [npc=15185]. It is a subgroup of the Bronze Dragonflight. Their leader, Nozdormu, sent these guardian factions to [zone=3606] where they guard the World Tree from another attack by the demons of Darkwhisper Gorge and help restore the time-stream and preserve the future of the world.\n\n[h3]Reputation[/h3]\nBoth bosses and trash monsters give reputation with each kill. [npc=17968], the final boss, awards 1500 reputation while the other four bosses give 375. General trash award 12 reputation, while [npc=17907] give 60. Yielding an average of 7800 per full clear, it would take 5-6 clears to reach exalted.\n\nCurrently some of the best [span class=q4][url=?items=4.-2&filter=na=band+of+the+eternal]rings[/url][/span] for raiding are available via this reputation. In order to recieve the rings, you must complete the previously required attunement quest, [quest=10445]. Each new reputation level awards an upgraded ring.'),(8,1011,0,NULL,0,2,'The [b]Lower City[/b] of [zone=3703] is the place where the refugees gather and help out in their own ways. When someone helps any of the mixture of races who fled from war, word gets around quickly. Their quartermaster, [npc=21655], is located at the market in the Lower City. The Lower City of Shattrath also contains a very useful Mana Loom or an Alchemy Lab. Many NPCs have extensive knowledge of crafting. The Battlemasters for both sides of all four [zones=6] can also be found here, as well as the World\'s End Tavern.\n\nOther important NPCs include:[ul][li]A neutral Grand Master Leatherworker, [npc=19187].[/li][li]A neutral Grand Master Skinner, [npc=19180].[/li][li]A neutral Grand Master Alchemist, [npc=19052], with an Alchemy Lab, who also gives the quest [quest=10902] (for alchemy specialization).[/li][li]Three specialist tailors who allow you to specialize and buy new epic tailoring recipes for armor sets and special bags (including the 20-slot bag).[ul][li][npc=22212] [small][/small] sells the patterns for the [itemset=553] set.[/li][li][npc=22213] [small][/small] sells the patterns for the [itemset=552] set.[/li][li][npc=22208] [small][/small] sells the patterns for the [itemset=554] set.[/li][/ul][/li][/ul]\n\n[h3]Reputation[/h3]\n[b]Until Honored[/b][ul][li]Run [zone=3790] in [i]normal[/i] mode, ~750 reputation.[/li][li]Run [zone=3791] in [i]normal[/i] mode, ~1250 reputation.[/li][li]Run [zone=3789] in [i]normal[/i] mode, ~2000 reputation.[/li][li]Turn in [item=25719] at [npc=22429].[/li][/ul][i]Note: Players aiming for faction higher than Honored should wait until honored to complete the Lower City quests.[/i]\n\n[b]Honored to Revered[/b][ul][li]Run Shadow Labyrinth in [i]normal[/i] mode, ~2000 reputation.[/li][li]Complete all available [url=?quests&filter=cr=1;crs=1011;crv=0]Lower City quests[/url].[/li][/ul][b]Revered to Exalted[/b][ul][li]Run Auchenai Crypts in [i]heroic[/i] mode, ~750 reputation.[/li][li]Run Sethekk Halls in [i]heroic[/i] mode, ~1250 reputation.[/li][li]Run Shadow Labyrinth in [i]normal[/i] or [i]heroic[/i] mode, ~2000 reputation.[/li][/ul]\n\n[h3]Trivia[/h3]\n[npc=19227], a vendor in Lower City, sells amulets which are very... interesting. He is quite the salesman, with items like [item=27940], which allows you to return to life as long as you return to the place you died. [i]Buyer beware![/i]\n\nAt exalted you can purchase a [item=31778]. Strangely, none of the NPCs in Lower City can be seen wearing one. Perhaps they cannot afford one...'),(8,1012,0,NULL,0,2,'The [b]Ashtongue Deathsworn[/b] are the elite of the Broken draenei tribe known as the Ashtongue. The Ashtongue tribe is led by the elder sage [npc=21700]; the Deathsworn are [i]officially[/i] aligned with [npc=22917] [small][/small]. The Deathsworn are Akama\'s most trusted lieutenants and are privy to their leader\'s mysterious motivations.\n\nTo discover the Deathsworn as a faction, the player must begin and complete the majority of the quest line which begins with Tablets of Baa\'ri ([quest=10568] / [quest=10683]). Eventually, you will speak with Akama, whereupon you will become Neutral with the Deathsworn.'),(8,1015,0,NULL,0,2,'The [b]Netherwing[/b] are a faction of dragons located in Outland. The unusual brood was spawned from the eggs of Deathwing\'s black dragonflight, and infused with raw nether-energies. Now, they seek to find their identity beyond the shadows of their father\'s destructive heritage.\n\n[h3]Reputation[/h3]\nPlayers are introduced to the Netherwing faction at 0/36000 hated reputation, and must be exalted to receive a [span class=q4][url=?items=15.-7&filter=na=Netherwing+Drake]Netherwing Drake[/url][/span]. The quest chain and reputation grind is a mostly solo endeavor involving quests that can only be completed once daily, a 5-player group quest on the way to neutral, and daily 3-player group quests after reaching revered. A flying mount is required for this reputation grind, and 300 riding skill is necessary to advance past neutral.\n\n[b]Hated to Neutral[/b]\nLevel 70 players will begin their journey to exalted reputation by picking up the quest chain offered by [npc=22113], a blood elf wandering the surface of the Netherwing Fields, in the southeast corner of [zone=3520]. The quest chain begins with the quest [quest=10804]. Completion of this quest line will provide an instant reputation boost to neutral and the choice of one of [span class=q3][url=?items&filter=qu=3;na=Netherwing+-wand]these[/url][/span] five items.\n\n[h3]Netherwing Reputation After Neutral[/h3]\nAfter completing the Kindness quest chain, Mordenai will be sure you have acquired 300 [spell=34091] skill and have you swear fealty to the Netherwing. This will grant you a Dragonmaw Fel Orc disguise when you enter Netherwing Ledge and allow you to communicate and work for the Dragonmaw stationed there. Mordenai will initially send you to [npc=23139] with a set of fake papers. Completing this quest will unlock the beginning Dragonmaw quests that you\'ll be working on to increase your Netherwing reputation. Most of these quests will have the new \"Daily\" tag added with 2.1. Daily quests differ from regular quests in that they are infinitely repeatable, but you may only complete each daily quest once per day and are restricted to ten total daily quests per day.[pad][i]Note: New quests will be unlocked with each reputation tier, and all daily quests of previous tiers will always be available, even after reaching exalted.[/i]\n\n[b][toggler id=Neutral hidden]Neutral[/toggler][/b]\n[div id=Neutral hidden]After turning in Mordenai\'s [item=32469] to Mor\'ghor to complete [quest=11013], your first group of quests will become available to start you on your way to the next tier of reputation with the Netherwing. Mor\'ghor will point you to the taskmaster to begin your grunt work, and [npc=23141] will reveal himself as a Netherwing ally in disguise and present another group of quests to you. One of which is [quest=11049]. Players will be able to turn in any [item=32506] that have a 1% chance to be found in [object=185881], [object=185877], and on almost all creatures on Netherwing Ledge. It can also be a rare find as a [object=185915] anywhere on Netherwing Ledge and in the Dragonmaw Fortress on the southeast corner of the Shadowmoon Valley mainland. This quest is not labeled as daily, and therefore can be done as many times as you can find eggs and will not hinder your daily quest limit.[pad]Other quests available from the beginning:[ul][li][i][small](Daily)[/small][/i] [quest=11018], [quest=11016], [quest=11017] - These will be available only to players who possess the respective profession to gather each item.[/li][li][i][small](Daily)[/small][/i] [quest=11015] - Simple gathering quest open to all players regardless of profession.[/li][li][i][small](Daily)[/small][/i] [quest=11020] - Yarzill will ask you to collect [item=32502] and use them to poison the peons that are working to gather resources for Dragonmaw.[/li][li][i][small](Daily)[/small][/i] [quest=11035] - You will need to fly to the northeast corner of Netherwing Ledge and position yourself on one of the floating rocks to intercept the [npc=23188] and recover 10 [item=32509].[/li][/ul][/div][pad][b][toggler id=Friendly hidden]Friendly[/toggler][/b]\n[div id=Friendly hidden]Mor\'ghor will award you with an [item=32694] to go with your new rank among the Dragonmaw.[ul][li][quest=11083] - [npc=23166] will task you with quelling the Murkblood Broken that are stationed deeper within the mines.[/li][li][quest=11081] - After finding [item=32726] in a [item=32724], you\'ll begin to reveal what\'s truly happening with the Murkblood in the mine.[/li][li][quest=11054] - [npc=23291] will have you fashion your very own [item=32680] for use in keeping the Dragonmaw peons in line and working at full efficiency.[/li][li][i][small](Daily)[/small][/i] [quest=11076] - The [npc=23149] will ask that you venture into the Netherwing mines and recover the cargo contained in mine carts randomly strewn among the interior of the mine.[/li][li][i][small](Daily)[/small][/i] [npc=23376] - One of the [npc=23376] will inform you that the creatures deeper in the mine are halting production and ask you to thin their numbers.[/li][li][i][small](Daily)[/small][/i] [quest=11055] - This humorous quest starts at Chief Overseer Mudlump after you bring him the required materials. You\'ll be able to fly around Netherwing Ledge and toss the Booterang at any [npc=23311] that can be found anywhere around the crystals of the ledge.[/li][/ul][/div][pad][b][toggler id=Honored hidden]Honored[/toggler][/b]\n[div id=Honored hidden]Mor\'ghor will award you with your new [item=32695], which is now usable anywhere as long as you\'re outside.[ul][li][quest=11063] - This six-part questline will have you in-flight following the other Dragonmaw masters of flight. They will all attempt to knock you off your mount with cleverly-placed air attacks, you must stay within vision range and on your mount until they land or you will fail and need to restart the quest. After defeating the last of the six riders, you\'ll be awarded a [item=32863], which functions exactly like a [item=25653]. The effects of the two trinkets do [b]not[/b] stack.[/li][li][quest=11089] - [npc=23427] will request a set of materials to fashion a special device to destroy his brother and hinder the Legion\'s advances from the Twilight Portal in western [zone=3518].[/li][li][i][small](Daily)[/small][/i] [quest=11086] - Mor\'ghor will send you to the Twilight Portal in Nagrand to kill 20 [url=?npcs&filter=na=deathshadow+-imp+-hound+-agent]Deathshadow Agents[/url]. Beware the overlords, they patrol most of the area and can pack quite a punch.[/li][/ul][/div][pad][b][toggler id=Revered hidden]Revered[/toggler][/b]\n[div id=Revered hidden]Mor\'ghor will award your final trinket upgrade, the [item=32864] after reaching revered.[ul][li]Kill Them All! ([quest=11094]/[quest=11099]) - Mor\'ghor will order you to begin the attack against your chosen faction\'s base of operations in Shadowmoon Valley. Obviously you\'re not going to actually allow the Dragonmaw to attack your allies, so report to the proper leader and unlock your final daily quest for Dragonmaw...[/li][li][i][small](Daily)[/small][/i] The Deadliest Trap Ever Laid ([quest=11097]/[quest=11101]) - Waves of Dragonmaw Skybreakers will attack after preparations are made. Bring allies, as this is a battle of attrition.[/li][/ul][/div][pad][b][toggler id=Exalted hidden]Exalted[/toggler][/b]\n[div id=Exalted hidden]After many days of work, finally the denouement of the Netherwing/Dragonmaw questline. Taskmaster Varkule will direct you to Mor\'ghor one last time, who will inform you that you will be promoted by [npc=22917] himself. Without spoiling the events that ensue, you will end up in Shattrath with your selection of Netherdrake epic mounts. You may choose one here for free, and if you decide on a different color later, you can speak with [npc=23489] back in the Dragonmaw Base Camp to buy another drake for 200 gold.[/div]'),(8,1031,0,NULL,0,2,'The [b]Sha\'tari Skyguard[/b] are an air wing of the [faction=935] of [zone=3703], defending the capital from attackers in the hills as well as battling against the arakkoa of Terokk in the peaks of Skettis. The Skyguard has two outposts, one in the northern reaches of the Skethyl Mountains and one near [faction=1038]. Players start out at neutral standing with the Skyguard.\n\n[h3]Reputation[/h3]\n[b]Daily Quests[/b][ul][li][quest=11008] - [npc=23048] will grant you a pack of explosives to destroy the eggs that rest atop Skettis structures.[/li][li][quest=11085] - A [npc=23383] can be found atop certain structures, players will escort him out for reputation, gold, and a choice of either 2 [item=28100] or 2 [item=28101].[/li][li][quest=11065] - [npc=23335] will inform you that the Skyguard\'s bombing runs have taken a toll on their mounts and ask you to gather some more Aether Rays to supplement their scout force.[/li][li][quest=11010] - [npc=23120] asks you to destroy the ammo for the Legion\'s flak cannons so the Skyguard Scouts can continue their job.[/li][li][quest=11004] - After collecting 6 [item=32388], [npc=23042] will make a potion that will allow vision of the more powerful arakkoa, such as [npc=23066].\n[i][small]Note: World of Shadows is not a daily quest, but may be repeated as many times as necessary.[/small][/i][/li][/ul][b]Creatures[/b][ul][li][npc=21804] - 5 reputation, up to the end of Revered.[/li][li][url=?npcs&filter=na=skettis+-kaliri+-assassin;minle=70]All Skettis Arakkoa[/url] - 10 reputation, regardless of Skyguard standing.[/li][li][npc=23029] - 30 reputation, regardless of Skyguard standing.[/li][/ul]'),(8,1038,0,NULL,0,2,'The [b]Ogri\'la[/b] are a faction of ogres in the [zone=3522], where their proximity to [item=32572] has allowed them to evolve past their brutish nature. They are currently fighting a war against both the Black Dragonflight and the Burning Legion, who seek the Apexis Crystals for their own purposes.\n\n[h3]Location[/h3]\nOgri\'la is situated near the western edge of Blade\'s Edge Mountains, between Forge Camp: Terror and Forge Camp: Wrath, just west of Sylvanaar. Ogri\'la is only accessible by flying mount/form. Another alternative is to have a reputation of honored or higher with [faction=1031]. But a player must have a flying mount to reach the Skyguard camp near Skettis.[pad]\n\n[h3]Reputation[/h3]\nReputation with Ogri\'la can only be gained via Quests, and there only repeatable quests are the available [url=?quests&filter=da=ja;cr=1;crs=1038;crv=0]daily quests[/url]. Thus, there is a cap on how much reputation a day a player can gain reputation with Ogri\'la, making it an \"ungrindable\" reputation.\n\n[b]Apexis Shards[/b]\n[item=32569] can be collected in a variety of ways. They can be looted from mobs, gathered from the environment, or they can be rewards from completed quests.[pad][b]Apexis Crystals[/b]\n[item=32572] are dropped from elite demons and dragons in Blade\'s Edge Mountains. In order to summon these mobs, 35 Apexis Shards are needed, and it is recommended that you have a 5 man group to defeat them.\n\n[b]Quests[/b]\nThere are a [url=?quests&filter=cr=1;crs=1038;crv=0]number of quests[/url] that a player can to do earn reputation with the Ogri\'la, as well as several [url=?quests&filter=da=ja;cr=1;crs=1038;crv=0]daily quests[/url]. Many of the daily quests will also grant reputation with the Sha\'tari Skyguard when they are first completed. \n\nIn order to access the main quests at Ogri\'la itself, a player must first complete the 5 group quests from [npc=22941].\n\n[h3]Depleted Items[/h3]\nA number of \"depleted\" items will sometimes drop from mobs. When combined with 50 Apexis Shards, the items [url=?search=Apexis+Crystal+Infusion]upgrade[/url], gaining stats and gem slots. Once the items are upgraded they become Bind on Equip, and can therefore be sold or traded to other players. One thing to note, however, is that although the depleted items may also have stats or effects, they cannot be equipped.'),(NULL,NULL,0,'sound&playlist',0,2,'Here you can set up a playlist of sounds and music. \n\nJust click the \"Add\" button near an audio control, then return to this page to listen to the list you\'ve created.'),(14,11,2,NULL,0,2,'[b]Aperçu :[/b] Les [b]Draenei[/b] sont des adeptes de Naaru et adorateurs de la Lumière Sainte. Chassées d’Argus, leur monde natal, les honorables Draeneï durent fuir des siècles durant Sargeras et sa Légion Ardente, après qu’il ait essayé de les corrompre. Les Draeneï ont alors trouvé une lointaine planète où s’établir. Ils appelèrent Draenor ce monde qu’ils partageaient avec les Orcs chamaniques. Une période de paix s’est alors installée.\nLa Légion Ardente fini par retrouver les DraeneÏ et corrompt les Orcs grâce à Guldan. Les Orcs partirent en guerre et exterminèrent les paisibles Draeneï. De rares survivants purent s’enfuir en Azeroth pour chercher de l’aide dans leur combat contre la Légion Ardente.\n\n[b]Capitale :[/b] Les Draeneï ont le siège de leur pouvoir dans les ruines de leur vaisseau : [zone=3557].\n\n[b]Zone de départ :[/b] [zone=3524] et [zone=3525] couvrent les tentatives des Draeneï de s’installer sur leurs nouvelles îles et de faire face à la corruption présente.\n\n[b]Montures :[/b] [npc=17584] vend des variétés d’Elekks, ainsi que [npc=33657] au tournoi d’Argent.'),(14,8,2,NULL,0,2,'[b]Aperçu :[/b] Les [b]Trolls[/b] Sombrelance vécurent à l\'origine dans les îles Brisées mais furent envahis par les nagas et les murlocs. Chassés de chez eux, la [url=?faction=530]tribu de Sombrelance[/url] se lie finalement d\'amitié avec les orcs qui ont sauvés les Trolls de la destruction. [npc=4949] leur offre l\'amnistie parmi la Horde, en contrepartie, la tribu Sombrelance jura fidélité au chef de guerre orque.\nBien qu\'ils refusent d\'abandonner leur sombre héritage, les féroces Trolls Sombrelance occupent une place d\'honneur au sein de la Horde.\n\n[b]Capitale :[/b] Les Trolls Sombrelance vivent maintenant dans la capitale de la Horde : [zone=1637].\n\n[b]Zone de départ :[/b] Les Trolls commencent leurs quêtes en [zone=14]\n\n[b]Montures :[/b] [npc=7952] au village de Sen\'jin vend de nombreux raptors ; [npc=33554], au tournoi d\'Argent, vend quelques modèles distincts.'),(14,10,2,NULL,0,2,'[b]Aperçu :[/b] Les [b]Hauts-Elfes[/b], race fière et hautaine, fondèrent jadis Quel’Thalas où ils créèrent une fontaine magique appelée Puits de Soleil. Ils profitèrent de sa puissance mais devinrent peu à peu dépendants de la magie. Si celle-ci devait être enlevée, les Hauts-Elfes soufreraient horriblement. Ils se séparèrent donc du reste de la société elfique.\nDe nombreux siècles plus tard, le fléau mort-vivant détruisit le Puit de Soleil et tua la plupart des Hauts-Elfes. Les survivants de l’assaut d’Arthas sur Lune-d’Argent, qui ont alors pris le nom d’Elfes de Sang, rebâtissent Quel’Thalas et cherchent de nouvelles sources de magie pour calmer leur douloureux manque.\nLes Elfes de Sang rejoignent la Horde à Burning Crusade.\n\n[b]Capitale :[/b] Les Elfes de Sang ont reconstruit [zone=3487].\n\n[b]Zone de départ :[/b] Les Elfes de Sang commencent au [zone=3430].\n\n[b]Montures :[/b] [npc=16264], aux Bois des Chants Eternelles, vend de nombreux faucons pèlerins ; [npc=33557], au tournoi d’Argent, vend quelques modèles uniques.'),(14,7,2,NULL,0,2,'[b]Aperçu :[/b] Les [b]Gnomes[/b], race excentrique, sont obsédés par les gadgets et la technologie. Malgré leur petite taille, ils ont mis à profit leur grande intelligence pour s\'assurer une place dans l\'Histoire.\nA l\'origine, les Gnomes viennent de la ville de [zone=721], qui était autrefois une merveille technologique mue à la vapeur. Malheureusement, la ville a été détruite par [npc=7937] à la suite d\'une tentative pour sauver la ville d\'une armée massive de Troggs.\nSes bâtisseurs sont désormais des vagabonds qui errent sur les terres des nains, venant en aide à leurs alliés du mieux qu\'il le peuvent.\n\n[b]Capitale :[/b] Aujourd\'hui, les Gnomes font leurs maisons à [zone=1537] malgré les efforts fournis pour reprendre leur bien aimée ancienne ville avec l\'[achievement=4786].\n\n[b]Zone de départ :[/b] Les Gnomes commencent à [zone=1], mais ont une séquence de quêtes très différente des Nains, couvrant Gnomeregan\n\n[b]Montures :[/b] [npc=7955] à Dun Morogh vend de nombreux mécanotrotteurs, ainsi que [npc=33650] au tournoi d\'Argent.'),(14,6,2,NULL,0,2,'[b]Aperçu :[/b] Les [b]Taurens[/b], race aux racines chamaniques profondes, sont des résidents de longue date de Kalimdor. Ils vouent un amour profond et durable à la nature, la grande majorité d’entre eux adorent une divinité connue sous le nom de la Terre Mère.\nRécemment attaqués par des centaures, les Taurens auraient été exterminés s’ils n’avaient pas rencontré, par hasard, les Orcs qui les aidèrent à repousser leurs ennemis. Afin d’honorer cette dette de sang, les Taurens ont rejoint la Horde, renforçant ainsi l’amitié entre les deux races.\n\n[b]Capitale :[/b] [zone=1638] est le lieu de résidence des Taurens\n\n[b]Zone de départ :[/b] Les Taurens commencent leurs quêtes en [zone=215].\n\n[b]Montures :[/b] [npc=3685] vend de nombreux kodos ; [npc=33556], au tournoi d’Argent, vend quelques modèles distinctifs.'),(14,5,2,NULL,0,2,'[b]Aperçu :[/b] Les [b]Réprouvés[/b], résultat d’une première attaque du Fléau en Azeroth, sont une métamorphose d’un certain nombre de membres de l’Alliance en mort vivant. Quand les forces combinées des Orcs, des Elfes, des Trolls, des Nains et des Humains se mirent à se défendre, [npc=36597] se mit à affaiblir ses armées en perdant le contrôle de certaines. Libérés de l’emprise du Roi Liche ainsi que des émotions gênantes et des liens de leurs vies humaines, les Réprouvés, menés par la banshee Sylvanas, réclament vengeance contre le fléau.\nLes Humain sont également devenus des ennemis, impitoyables dans leur désir de purger les terres de tous les mort-vivants. \nLes Réprouvés ne se soucient que très peu de leurs alliés. La Horde ne représente à leurs yeux qu’un simple outil qui pourrait servir leurs sombres desseins.\n\n[b]Capitale :[/b] Les Réprouvés résident sous les ruines de l’ancienne ville humaine de Lordaeron : la [zone=1497].\n\n[b]Zone de départ :[/b] Tous les joueurs de Réprouvés commencent dans la [zone=85]. Ils sont élevés par les Val’kyrs comme des réprouvés de seconde génération\n\n[b]Montures :[/b] [npc=4731] vend de nombreux chevaux mort-vivants ; [npc=33555], au tournoi d’Argent, vend quelques modèles distincts.'),(14,4,2,NULL,0,2,'[b]Aperçu :[/b] Les [b]Elfes de la nuit[/b], race ancienne et mystérieuse, vivaient à Kalimdor pendant des milliers d\'années, ils fondèrent un vaste empire, mais leur usage imprudent de la magie les conduisit à leur perte. Pétris de douleur, ils se retirèrent dans les forêts et demeurèrent ainsi isolés jusqu\'au retour de leur ancien ennemi. Ne disposant d\'aucune alternative, les Elfes de la nuit furent contraints de sacrifié l\'arbre monde afin d\'arrêter l\'avancé de la Légion Ardente. \nIls émergèrent de leur isolement, afin de défendre leur place dans le nouveau monde.\n\n[b]Capitale :[/b] La capitale des Elfes de la nuit est [zone=1657], située dans les branches de l\'arbre monde.\n\n[b]Zone de départ :[/b] Les Elfes de la nuit commencent à [zone=141]\n\n[b]Montures :[/b] [npc=4730], à Darnassus, vent une variété de sabre de nuit, ainsi que [npc=33653] au tournoi d\'Argent.'),(14,3,2,NULL,0,2,'[b]Aperçu :[/b] Les [b]Nains[/b], race robuste, viennent de Khaz Modan dans les Royaumes de l’Est. Par la passé, les Nains ne s’intéressaient qu’aux richesses extraites des profondeurs de la terre. Lorsque des études semblèrent indiquer que les Nains étaient les descendants d’une race proche des Titans qui leur aurait conféré un héritage enchanté, la curiosité des Nains fut piquée au vif. Décidés à en savoir plus, les Nains commencèrent à rechercher des artefacts perdus et des connaissances disparues. Aujourd’hui, les Nains dirigent des fouilles archéologiques partout dans le monde.\nTrois principaux Clans de Nains sont répartis dans tout Azeroth : Les Barbes de Bronze, Les Marteaux Hardis et les Sombrefers.\n\n[b]Capitale :[/b] Les Nains font leur maison dans leur siège ancestral de [zone=1537].\n\n[b]Zone de départ :[/b] Les Nains commencent à [zone=1].\n\n[b]Montures :[/b] [npc=1261] vend des béliers à la ferme des Amberstill, ainsi que [npc=33310] au tournoi d’Argent.'),(14,1,2,NULL,0,2,'[b]Aperçu :[/b] Les [b]Humains[/b], race la plus jeune et la plus peuplés d\'Azeroth, maîtrisent les arts du combat, l\'artisanat et la magie avec une efficacité stupéfiante. La valeur et l\'optimisme des Humains les ont conduits à bâtir certains des plus grands royaumes du monde. En cette ère de troubles, après des générations de conflit, l\'Humanité aspire à ranimer sa gloire passée et à se forger un nouvel avenir rayonnant.\nLes Humains, aux talents très variés, sont devenus les chefs de l\'Alliance grâce à leurs ambitions et leurs résiliences. \n \n[b]Capitale :[/b] Le siège du pouvoir Humain est dans la ville reconstruite de [zone=1519].\n \n[b]Zone de départ :[/b] Les Humains commencent leurs quêtes dans la [zone=12].\n \n[b]Montures :[/b] [npc=384] vend des palefrois dans Hurlevent, et [npc=33307], au tournoi d’Argent, vend quelques modèles distincts.'),(14,2,2,NULL,0,2,'[b]Aperçu :[/b] Les [b]Orcs[/b] étaient, à l\'origine, un peuple pacifique aux croyances chamaniques résidant sur le monde de Draenor. Malheureusement, infectés par le sang démoniaque de Mannoroth le destructeur, les Orcs furent réduit en esclavage par la Légion Ardente, contraint de guerroyer contre les Draenei et de conquérir Azeroth. \nAprès de nombreuse années de joug, les Orcs ont réussi à se libérer de l\'emprise démoniaque et ont conquis leur liberté, pour revenir à leurs racines chamaniques.\nMaintenant, sous la direction de leur nouveau chef de guerre, les Orcs se construisent un nouveau foyer, où ils combattent pour l\'honneur, dans un monde étranger, haïs et calomniés.\n\n[b]Capitale :[/b] Les Orcs résident maintenant dans la ville d\'[zone=1637], du nom du défunt Orgrim Doomhammer, ancien chef de guerre de la Horde.\n\n[b]Zone de départ :[/b] Les Orcs commencent leurs quêtes en [zone=14].\n\n[b]Montures :[/b] [npc=3362], à Orgrimmar, vend une variété de loups ; [npc=33553], au tournoi d\'Argent, vend quelques montures distinctives'),(NULL,NULL,0,'reputation',0,2,'[b]Reputation[/b] is a rough measurement of how much you participate in the community--it is earned by convincing your peers that you know what you’re talking about. Our community puts just as much work as our developers do into making our site as awesome as it is and reputation is meant as a way for you to track just how much work you\'re putting into us.\r\n\r\nThe primary means of gaining reputation is by posting quality comments on database entries (which are then voted up by other site members) and by general contributions to the site which can include actions like data and screenshot submissions. Whenever you leave a comment on a database entry, your peers can then vote on these comments, and those votes will cause you to gain reputation. You can also earn reputation by voting on other users\' comments and by sending in reports!\r\n\r\nBy being a good-standing and contributing user you will be able to earn both reputation and achievements for many of the same actions!\r\n\r\n[h3]Reputation Gains[/h3]\r\n[div style=\"max-width:400px\"][table class=grid]\r\n[tr][td][url=?account=signup]Registering[/url] an account[/td]\r\n[td align=right class=no-wrap]CFG_REP_REWARD_REGISTER reputation[/td]\r\n[/tr]\r\n[tr][td]Daily visit[/td]\r\n[td align=right class=no-wrap]CFG_REP_REWARD_DAILYVISIT reputation[/td]\r\n[/tr]\r\n[tr][td]Posting a comment[/td]\r\n[td align=right class=no-wrap]CFG_REP_REWARD_COMMENT reputation[/td]\r\n[/tr]\r\n[tr][td]Your comment was voted up (each upvote)[/td]\r\n[td align=right class=no-wrap]CFG_REP_REWARD_UPVOTED reputation[/td]\r\n[/tr]\r\n[tr][td]Submitting a screenshot[/td]\r\n[td align=right class=no-wrap]CFG_REP_REWARD_SUBMIT_SCREENSHOT reputation[/td]\r\n[/tr]\r\n[tr][td]Suggesting a video[/td]\r\n[td align=right class=no-wrap]CFG_REP_REWARD_SUGGEST_VIDEO reputation[/td]\r\n[/tr]\r\n[tr][td]Submitting a guide (approved)[/td]\r\n[td align=right class=no-wrap]CFG_REP_REWARD_ARTICLE reputation[/td]\r\n[/tr]\r\n[tr][td]Filing a report (accepted)[/td]\r\n[td align=right class=no-wrap]CFG_REP_REWARD_GOOD_REPORT reputation[/td]\r\n[/tr]\r\n[/table][/div]\r\n\r\n\r\n[h3]Site Privileges[/h3]\r\nThe higher your reputation level, the more privileges you gain. Earn a high enough reputation to unlock additional rewards, in the form of new privileges around the site!\r\n[pad]\r\n[div style=\"max-width:400px\"][table class=grid]\r\n[tr][td]Post comments[/td]\r\n[td align=right class=no-wrap]CFG_REP_REQ_COMMENT reputation[/td]\r\n[/tr]\r\n[tr][td]Upvote on comments[/td]\r\n[td align=right class=no-wrap]CFG_REP_REQ_UPVOTE reputation[/td]\r\n[/tr]\r\n[tr][td]Downvote on comments[/td]\r\n[td align=right class=no-wrap]CFG_REP_REQ_DOWNVOTE reputation[/td]\r\n[/tr]\r\n[tr][td]More votes per day[/td]\r\n[td align=right class=no-wrap]CFG_REP_REQ_VOTEMORE_BASE reputation[/td]\r\n[/tr]\r\n[tr][td]Comment votes worth more[/td]\r\n[td align=right class=no-wrap]CFG_REP_REQ_SUPERVOTE reputation[/td]\r\n[/tr]\r\n[/table][/div]\r\n[pad]\r\n[url=?privileges]Check out full details on site privileges you can earn![/url]\r\n'),(NULL,NULL,0,'privilege=1',0,2,'[h3]Reputation required for posting comments?[/h3]\nThe very first privilege you can earn is the ability to post comments. Because this privilege requires only CFG_REP_REQ_COMMENT reputation, it is earned soon upon registering an account (which awards CFG_REP_REWARD_REGISTER reputation)! Keep this in mind if you\'ve recently registered to post on a contest thread.\n\n[h3]How do I post a comment?[/h3]\nOnce you have earned the ability to post comments, it\'s easy to do! Got some interesting information about an item? Strategies for earning an achievement or killing a boss? These are just a few examples of what could make a quality comment here!\n\nSimply visit any database page that you wish to leave a comment on and scroll down to the \'Contribute\' section. In the \'Add your comment\' tab, you can easily write and format your database comment. You can use our handy formatting buttons to improve the visual quality of your post, and easily add database links using the \'Links\' menu and entering database entry IDs. Once you\'re done, simply click the \'Submit\' button below and voila!\n\n[h3]Comment rating and you![/h3]\nAll comments made on database pages are subject to our rating system. This allows users who have reached the appropriate reputation level to upvote and downvote comments based on their quality. Making quality comments will earn you website reputation each time it has been upvoted, but make a poor quality comment and you may end up losing reputation if it is downvoted!\n\nFor more information on commenting, be sure to check out our handy [url=?help=commenting-and-you]Commenting and You[/url] guide in the website help section!'),(NULL,NULL,0,'privilege=2',0,2,'[h3]Posting External Links[/h3]\nOne of the first privileges allowed to users is the ability to post external links on the site. This will allow you to link to relevant information found on other websites from our database as well as in our forums. You can also add a link to your user profile, such as to your guild website or personal blog. Users without the appropriate reputation level will have their links filtered automatically, to help prevent spammers and malicious links from being posted on our website.\n\n[h3]Posting Policy[/h3]\nPlease be aware that some URLs may still be filtered out by our moderation team, as they made be deemed inappropriate or advertising. If you are uncertain whether or not a link will be considered advertisement, please do not hesitate to contact our Feedback team with any questions!\n'),(NULL,NULL,0,'privilege=4',0,2,'[h3]No CAPTCHAs[/h3]\nAh, CAPTCHAS. Love \'em or hate \'em, they\'re often a necessary evil for popular websites which allow any sort of user contribution. Here, we use [url=https://www.google.com/recaptcha/intro/index.html]ReCAPTCHA[/url] which helps thwart bots and spammers from abusing our forum and comment systems. Unfortunately, this also creates a minor inconvenience for our more active users, who are still occasionally asked to input a CAPTCHA despite long since establishing themselves as a legitimate member of the community. Well, not anymore! Users who reach the appropriate reputation level will no longer have to enter CAPTCHAs anywhere on the site!\n'),(NULL,NULL,0,'privilege=5',0,2,'[h3]Comment rating value increase[/h3]\nWhen you have reached a higher reputation level, your contributions to the site will raise in value! As a more trusted member of our community, your comment ratings will now have an increased weight and, as a result, have a greater effect on the total rating of a comment! Your vote contribution are doubled, so each of upvote will count as two votes (and each of your downvotes as two, as well)! This will allow higher reputation users to have more of an effect on considering quality of a comment, raising quality comments higher and lowering poor comments faster.\n'),(NULL,NULL,0,'privilege=9',0,2,'[h3]More votes per day[/h3]\nWe have a daily cap for comment votes set to CFG_USER_MAX_VOTES.\n\nThis privilege instantly increases the cap by 1, and then increases the cap by an additional 1 point for each CFG_REP_REQ_VOTEMORE_ADD reputation you have above CFG_REP_REQ_VOTEMORE_BASE.\n'),(NULL,NULL,0,'privilege=10',0,2,'[h3]Upvoting Comments[/h3]\nDid you find a comment particularly insightful or laugh out loud funny? Upvote it then! Upvoting is a way of giving props to those who truly contribute. From small guides to witty jokes, if a comment has enhanced your user experience, you should remember to upvote it.\n\nThe higher amount of upvotes a comment has, the higher up on the page it is. This way the community can help determine what comments are worth reading by sending some upvotes their way.\n\n[h3]Upvoting Policy[/h3]\nYou should not use upvotes to reward your friends or withhold upvotes to punish users you dislike. These are bannable offenses and you will probably lose your ability to upvote if we catch you doing it.\n'),(NULL,NULL,0,'privilege=11',0,2,'[h3]Downvoting Comments[/h3]\nDid you find a comment that was out of date, irrelevant, or otherwise less than useful? Downvote it then! Downvoting is a way of removing the clutter from the database and ensuring our comments are up to date. Downvotes remove an upvote--and if a comment has too many downvotes, it can even become a negative comment which appear at the end of an article rather than the beginning. \n\n[h3]Downvoting Policy[/h3]\nYou should not use downvotes to punish users you dislike nor should you downvote in quick succession. Try to use downvotes only to help us out, leaving personal bias out of it. If you abuse downvotes either by making too many in a short time frame or targeting a specific user, you may be warned and in some cases banned.\n'),(NULL,NULL,0,'privilege=12',0,2,'[h3]Replying to a Comment[/h3]\nYou can reply to comments easily and quickly with the new commenting system. All you have to do is leave a reply on an existing comment for this to work.\n\nA reply is best used to illustrate alternatives to a comment, highlight its accuracy, or expand on a joke. For example, if someone says an item drops from a certain boss but you know it does not, you could reply to explain it doesn\'t; it\'s likely people will find your comment helpful so they don\'t waste time trying to get the item from that NPC.\n\nPlease be aware that you should not use comments like forum threads for discussion.\n'),(NULL,NULL,0,'privilege=13',0,2,'Your avatar on the [url=CFG_BOARD_URL]Forums[/url] has an uncommon-quality green border.'),(NULL,NULL,0,'privilege=14',0,2,'Your avatar on the [url=CFG_BOARD_URL]Forums[/url] has a rare-quality blue border.'),(NULL,NULL,0,'privilege=15',0,2,'Your avatar on the [url=CFG_BOARD_URL]Forums[/url] has an epic-quality purple border.'),(NULL,NULL,0,'privilege=16',0,2,'Your avatar on the [url=CFG_BOARD_URL]Forums[/url] has a legendary-quality orange border.'),(NULL,NULL,0,'privilege=17',0,2,'[img src=STATIC_URL/images/premium/user-badge.png border=0 float=right]Unlock [url=HOST_URL/?premium]AoWoW Premium[/url] status for free.\n\nAs a Premium user, you can access a variety of perks:\n[ul]\n[li]Images in tooltips[/li]\n[li]Additional avatar borders[/li]\n[li]And much more![/li][/ul]\n\n'),(13,1,2,NULL,0,2,'[b][color=c1]Les Guerriers[/color][/b] sont une classe très puissante, avec la capacité de taner ou d\'infliger des dégâts de mêlée. Sa caractéristique principale est la force, mais les tanks s\'intéresseront également à l\'Endurance.\n\nCe combattant se bat avec une posture ce qui lui permet l\'accès à différentes capacités et lui accorde des bonus. Il utilisera [spell=71] pour tanker (appris au niveau 10) et [spell=2457] (appris au niveau 1) ou [spell=2458] (appris au niveau 30) pour les dégâts en mêlée.\n\nL\'arbre de protection du Guerrier contient de nombreux talents pour améliorer leur survie et générer des menaces contre les monstres. Les Guerriers de protection sont l\'une des principales classes de tank du jeu. Pour aller au combat, ils peuvent utiliser [spell=100] ou [spell=20252] mais seul le Guerrier protection peut protéger un allié en utilisant [spell=3411].\nIls ont également deux arbres de talent orientés sur les dégâts [icon name=ability_rogue_eviscerate][url=spells=7.1.26]Armes[/url][/icon] et [icon name=ability_warrior_innerrage][url=spells=7.1.256]Fury[/url][/icon], ce dernier comprend le talent [spell=46917], qui permet au Guerrier de manier deux armes à deux mains. Les Guerriers sont capable de faire de gros dégâts de zone avec des sorts tels que [spell=845], [spell=1680] et [spell=46924]. \n\nLe Guerrier porte une armure en plaques et aspire à la perfection dans les combats. Lorsqu\'il inflige ou subit des dégâts, il génère de la rage, utilisée pour alimenter ses attaques spéciales.\n[ul]\n[li] Allié utile, qui peut ajouter des buffs au groupe ou raid avec [spell=6673] et [spell=469], mais seul les Guerriers Fury peuvent fournir un buff passif [spell=29801] qui augmente les coups critiques en mêlée et à distance.[/li]\n[li] L\'avantages uniques des Guerriers, ce sont les 3 postures de combats.[/li]\n[li] Il peut choisir de se spécialiser dans le port d’armes à deux mains, d\'arme à une main, ou dans l\'utilisation du bouclier en plus d\'une arme à une main.[/li]\n[li] Et dispose de plusieurs techniques qui permettent de se déplacer rapidement sur le champ de bataille.[/li]\n[/ul]'),(13,2,2,NULL,0,2,'[b][color=c2]Les Paladins[/color][/b] sont des combattants qui utilisent la magie du sacré pour soigner les blessures et combattre le mal. Ils sont relativement autonomes et disposent de nombreuses techniques destinées à empêcher les morts. Le paladin peut choisir de se battre, de protégés ou de soigner, il utilisera le mana pour combattre le mal. Ses caractéristiques principales dépendent du rôle choisi.\n\nIl est un mélange d’un combattant en mêlée et d’un lanceur de sorts secondaires. Allié indispensable dans un combat, il renforce leurs amis avec de saintes auras (une aura active par paladin sur chaque membre du raid) et des bénédictions spécifiques pour les protéger du mal et renforcer leurs pouvoirs.\n\nPortant de lourdes armures, ils peuvent résister à des coups terribles dans les batailles les plus dures tout en guérissant leurs alliés blessés et en ressuscitant les morts. Au combat, ils peuvent utiliser des armes à deux mains, paralyser leurs ennemis, détruire des morts vivants et des démons, et les juger avec une sainte vengeance.\nLes paladins sont une classe défensive, principalement conçus pour survivre à leurs adversaires, grâce à leur assortiment de capacités défensives. Ils font aussi d’excellents tanks en utilisant leurs capacités [spell=25780].\n\n[ul]\n[li] Classe pouvant guérir, tanker avec leur précieux bouclier et infliger des dégâts en mêlée.[/li]\n[li] Renforce les alliées avec les [url=spells=7.2&filter=na=aura]Auras[/url], les [url=spells=7.2&filter=na=bénédiction]bénédictions[/url] et d’autres buffs.[/li]\n[li] Seule classe avec un véritable sort d’invulnérabilité [spell=642].[/li]\n[/ul]\n\n[b]Montures de classe :[/b]\n[ul]\n[li] [spell=13819] est un destrier royal que seuls les plus fervents des paladins peuvent appeler à leur service. Niveau 20 - Bonus de Vitesse de 60%. [/li]\n[li] [spell=23214] est un équipier infatigable capable d\'amener son valeureux maître dans tout Azeroth. Niveau 40 - Bonus de vitesse de 100%. [/li]\n[/ul]'),(13,4,2,NULL,0,2,'[b][color=c4]Les Voleurs[/color][/b] sont une classe de mêlée capable d\'infliger de grandes quantités de dégâts à leurs ennemis avec des attaques rapides en utilisant de l\'énergie comme ressources. Leurs caractéristiques principales sont la puissance d\'attaque et l\'agilité.\n\nLes Voleurs ont un puissant arsenal de compétences, dont beaucoup sont renforcés par leur capacité de furtivité et d\'étourdissement de leurs victimes. Capables d\'utiliser des poisons, ils paralysent leurs adversaires, les affaiblissant massivement dans la bataille. Avec l\'ambidextrie, ils peuvent utiliser une large gamme d\'armes, mais les Voleurs privilégient la dague, qui est la plus représentative de cette classe. \n\nCe sont les maîtres pour se déplacer furtivement autour de leurs ennemis, frapper dans l\'ombre un adversaire pour tenter de l\'achever rapidement puis s\'échapper du combat en un clin d’œil. \nIls endossent donc souvent le rôle d\'assassin ou d\'éclaireur, mais nombre d\'entre eux sont des loups solitaires.\n\n[ul]\n[li] Porte des armures en cuir.[/li]\n[li] Porte une arme dans chaque main.[/li]\n[li] Utilise une grand variété d\'armes de mêlée, comme les poignards, les armes de pugilats, les masses à une main, les épées à une main et les haches à une main.[/li]\n[li] Recouvre leurs armes avec du [url=items=0.-3&filter=na=poison;ub=4]poison[/url] pour gravement affaiblir leurs ennemis.[/li]\n[li] Utilise le [spell=1784] pour n’être visible que par les ennemis les plus perspicaces.[/li]\n[li] Cumule 5 points de combo pour infliger de puissants coups de grâce.[/li]\n[/ul]'),(13,3,2,NULL,0,2,'[b][color=c3]Les Chasseurs[/color][/b] sont une classe très unique dans le monde de World of Warcraft. C\'est la seule classe non-magique qui fait des dégâts à distance. Ils se battent avec des arcs, des armes à feu ou des arbalètes. Leurs caractéristiques principales sont la puissance d\'attaque et l\'agilité.\n\nLes Chasseurs se sentent chez eux dans la natures et ont une affinité spéciale avec les animaux. Il sait apprivoiser son propre [url=pets]familier[/url] qui l\'aidera à vaincre son ennemi. L\'animal du chasseur est unique, il possède un arbre de talent où le Chasseur peut attribuer des points dans des compétences diverses et des capacités passives. Chaques espèces de familier a une capacité spéciale unique. Le Chasseur peut rechercher les bêtes les plus appréciables en fonction de leurs apparences ou capacités. Seuls certains familiers ne sont accessibles que si le Chasseur choisi dans son arbre de talent [icon name=ability_hunter_beasttaming][url=spells=7.3.50]Maîtrise des bêtes[/url][/icon] qui lui donne accès aux bêtes « exotique » tels que [pet=46] ou [pet=39].\n\nPendant que leurs familiers attaques, les Chasseurs font pleuvoir leurs projectiles sur leurs malheureuses cibles. Ils préfèrent s’évader du corps-à-corps et ralentir leurs ennemis pour s\'éloigner et lancer leurs salves mortelles. Ils sont aussi capable de poser des pièges pour infliger des dégâts, ralentir ou rendre impossible toutes actions de leurs ennemis.\n\nLes Chasseurs portent des armures intermédiaires (cuir/maille) et utilisent le mana pour faire des dégâts.\n[ul]\n[li] Il peut voyager très vite en utilisant [spell=13161] et le partager avec [spell=13159].[/li]\n[li] Ils ont un certain nombre de compétence accès sur la survie qu\'ils peuvent utiliser pour échapper ou éviter un danger potentiel, comme [spell=5384] et [spell=781].[/li]\n[li] Les Chasseurs spécialisés dans la [icon name=ability_hunter_swiftstrike][url=spells=7.3.51]Survie[/url][/icon] peuvent avoir [spell=53292], ce qui leur permet de fournir aux membres du raid le [spell=57669].[/li]\n[/ul]'),(13,5,2,NULL,0,2,'[b][color=c5]Les Prêtres[/color][/b] sont généralement considérés comme l\'une des classes de soins les plus répandus dans World of Warcraft, car ils ont deux arbres de talents qui peuvent être utilisés pour guérir très efficacement. Les caractéristiques principales sont la puissance des sorts, l\'intelligence et l\'Esprit (s\'il s\'est spécialisé dans les soins).\n\nL\'arbre [icon name=spell_holy_holybolt][url=spells=7.5.56]Sacré[/url][/icon] comprend des talents qui renforcent fortement la guérison faite à leurs alliés, y compris des sorts qui peuvent être utilisés pour guérir plusieurs joueurs à la fois, comme [spell=48089]. \nL\'arbre de talent [icon name=spell_holy_wordfortitude][url=spells=7.5.613]Discipline[/url][/icon] se concentre principalement sur l\'absorption et l\'atténuation des dommages grâce à l\'utilisation de [spell=48066] et réduit les dégâts subis avec [spell=63944].\n\nLes Prêtres disposent d\'une grande palette d\'outils pour soigner, mais ils peuvent également sacrifier leurs soins pour infliger des dégâts grâce à la magie de l\'[icon name=spell_shadow_shadowwordpain][url=spells=7.5.78]Ombre[/url][/icon]. Ils sont alors capables d\'infliger des dégâts importants avec leurs capacités uniques et une fois qu\'ils se mettent en [spell=15473], leurs dégâts d\'ombre augmentent de manière significative tout en perdant la capacité de lancer des sorts du sacré.\n\nIl porte une armure en tissus, soigne les dégâts grâce à la magie du sacré mais inflige des dégâts grâce à la magie de l\'Ombre. Il utilise le mana comme ressource.\n[ul]\n[li] Fournissant les buffs les plus appréciés dans le jeu - [spell=48161], qui donne un buff d\'endurance indispensable à tout raid. Ils peuvent utiliser [spell=48073] et [spell=48169].[/li]\n[li] Les prêtres d\'ombre sont très sollicités dans n\'importe quel raid , fournissant le buff [spell=57669] pour stimuler la régénération de mana et peut même guérir leur propre groupe avec [spell=15286].[/li]\n[/ul]'),(13,8,2,NULL,1,2,'[b][color=c8]Les Mages[/color][/b] sont les utilisateurs emblématiques de la magie en Azeroth, qui apprennent leur art au cours de leurs recherches et études approfondies. Ils maîtrisent la magie du feu, du givre et des arcanes pour détruire ou neutraliser leurs ennemis. Leurs caractéristiques principales sont la puissance des sorts et l’intelligence.\n\nIls portent des armures légères, mais compensent cette faiblesse par une puissante gamme de sorts offensifs et défensifs. Le mage fait donc des gros dégâts à distance, envoyant des boules élémentaires sur un ennemi isolé mais faisant pleuvoir la destruction sur une armée. En cas d\'attaque, il peut échapper aux combats rapprochés avec [spell=1953] et devient un [spell=45438] quand cela devient trop critique.\n\nLes Mages peuvent également augmenter les pouvoirs de leurs alliés : [spell=23028], les inviter à leurs [spell=43987] et même les faire voyager à travers des [url=spells=7.8.237&filter=na=portail]portails[/url]. Classe indispensable pour voyager en toute tranquillité. Ils utilisent le mana comme ressource. Les Mages :\n[ul]\n[li]Transforment leurs ennemis en créatures inoffensives ou les geler sur place grâce à [spell=122].[/li]\n[li]Utilisent [item=50045] pour avoir un élémentaire d\'eau en familier.[/li]\n[/ul]'),(13,6,2,NULL,0,2,'[b][color=c6]Les Chevaliers de la mort[/color][/b] sont d\'anciens agent du Fléau, désormais alliés avec la Horde ou l\'Alliance. Cette classe de héros débute le jeu à haut niveau (55). Ses caractéristiques principales sont la force, sans oublier l\'endurance pour les tanks.\n\nTous leurs arbres de talent peuvent être utilisés pour faire des dégâts ou tanker.\n\nLes Chevaliers de la mort qui ont une affinité avec le [icon name=spell_deathknight_bloodboil][url=spells=7.6.770]Sang[/url][/icon] ont une grande capacité d’auto-guérison et peuvent fournir à un allié : [spell=49016] qui l’enrage à la vue du sang du champ de bataille.\nL’arbre de talent [icon name=spell_frost_freezingbreath][url=spells=7.6.771]Givre[/url][/icon] permet une augmentation significative de l’armure et spécialise le Chevalier de la mort dans les dégâts de zone avec [spell=49184]\nLes maîtres des maladies et des invocations sont les chevaliers de la mort [icon name=spell_deathknight_armyofthedead][url=spells=7.6.772]Impie[/url][/icon]. Ils peuvent utiliser leurs talents [spell=52143] et [spell=49206] pour être aidé lors des combats. Ils ont aussi une plus grande résistance à la magie grâce à la [spell=51052].\n\nLe chevalier de la mort utilise des runes comme ressource principale, dont chacun des trois types est utilisé pour différentes techniques.\n[ul]\n[li] Ils se battent avec les présences (semblable aux positions d\'un Guerrier) qui fournit des bonus spéciaux à leurs rôles.[/li]\n[li] Il dispose de plus de capacités à distance que la plupart de classes de corps à corps et privilégie les maladies et les dégâts infligés par ses familiers morts-vivants.[/li]\n[li] La classe de chevalier de la mort a sa propre capacité d\'enchantement d\'arme spéciale appelée [spell=53428], ce qui remplace le besoin d\'enchantements d\'armes classiques.[/li]\n[li] Ont accès à une zone spéciale inscrite inaccessible par toutes les autres classe : Acherus, le fort d’ébène, situé dans [zone=4298]. Où ils gagneront leurs points de talent en tant que récompenses de quêtes dans les premières heures de jeux.[/li]\n[/ul]\n\n[b]Montures de classe :[/b]\n[ul]\n[li] [spell=48778] - Niveau 55 - Bonus de Vitesse de 100%. [/li]\n[li] [spell=54729] - Niveau 60 - Bonus de vitesse : s’adapte à la compétence de monte. [/li]\n[/ul]'),(13,7,2,NULL,0,2,'[b][color=c7]Les Chamans[/color][/b], maîtres des éléments et de la nature, apportent un grand nombre de buffs à tout un groupe sous forme de totem. Un Chaman peut appeler un totem de chaque élément : terre, feu, eau et air. Ces totems apparaissent à leurs pieds et sont actifs pour toutes les personnes du raid se trouvant dans la zone d’effet du totem. Un bon Chaman sait quels totems sont à lancer et dans quelles circonstances les utiliser, pour maximiser les dégâts du groupe et la survie.\n\nIls sont principalement des lanceurs de sorts, bien qu’un Chaman [icon name=spell_nature_lightningshield][url=spells=7.7.373]Amélioration[/url][/icon] aime se rapprocher des ennemis pour faire de gros dégâts. Il apprend l’[spell=30798] et peut utiliser le sort [spell=51533] pour invoquer 2 Esprits de Loups qui combattent avec lui. Bien qu’il soit principalement de mêlée, le Chaman Amélioration peut bénéficier de la puissance des sorts et lancer instantanément [spell=403] ou des soins avec le talent [spell=51530]. \n\nLes Chamans [icon name=spell_nature_lightning][url=spells=7.7.375]Élémentaires[/url][/icon] se tiennent en retrait pour lancer leurs sorts de feu et de foudre et infliger de grandes quantités de dégâts. Ils peuvent repousser leurs ennemis avec [spell=51490] et aussi les enraciner avec [spell=51486]. Ils apportent le [icon name=spell_fire_totemofwrath][url=spell=57722]Totem de courrou[/url][/icon] et le [spell=51470], buffs très recherchés dans les raids.\n\nLes Chamans qui choisissent [icon name=spell_nature_magicimmunity][url=spells=7.7.374]Restauration[/url][/icon] ont un grand panel de sort de guérison se qui leurs permets de se spécialiser dans le soin mono-cible ou multi-cible. Ils sont reconnus pour leurs puissantes [spell=1064] et pour créer un [spell=16190] qui aide la restauration de mana aux membres de leurs groupes. Ils gagnent aussi un puissant [spell=974], peuvent employer [spell=51886] pour enlever les malédictions, et ont un sort de guérison instantané : [spell=61295] qui soigne aussi au fil du temps.\n\nLes Chamans invoquent la puissance des éléments pour améliorer les dégâts de leurs armes ou sorts. Ils portent des armures moyennes, boucliers et utilisent le mana comme ressources.\n[ul]\n[li] Il peut apprendre plus de 20 totems différents.[/li]\n[li] Peuvent lancer [spell=32182] (ou [spell=2825]) pour amplifier les dégâts et les soins de tout le raid. Un buff unique très recherché.[/li]\n[li] Un chaman peut se transformer en [spell=2645] à partir du niveau 16 et peut même le rendre instantané avec le talent [spell=16287]. Ce sort ne peut être utilisé qu\'en extérieur.[/li]\n[li] Il ne peut avoir qu\'un seul bouclier élémentaire d\'actif sur lui [spell=324] ou [spell=52127]. Le [spell=974], peut-être posé sur un autre joueur.[/li]\n[/ul]'),(13,11,2,NULL,0,2,'[b][color=c11]Les Druides[/color][/b] sont la « classe à tout faire » de World of Warcraft, c\'est-à-dire, capable de remplir tous les rôles : soigner, faire des dégâts à distance, faire des dégâts de mêlée ou tanker, en utilisant le Changeforme. Le druide offre donc aux joueurs de nombreux styles de jeu. Ses caractéristiques principales dépendent du rôle choisi.\n\nSous sa forme normale, c’est un lanceur de sorts qui peut se battre à distance et se soigner. Mais il peut aussi prendre d’autres formes dont des formes animales :\n\nLorsqu’un druide se transforme en [spell=5487] (et à un niveau plus avancé, [spell=9634]), son mana se change alors en rage, capable de charger sa cible, de la [spell=8983] et de subir des coups de plusieurs adversaires simultanément. C’est une forme orientée vers le tanking qui fournit une armure et de la vie supplémentaire. Il peut esquiver les coups, utiliser [spell=22812] pour augmenter sa résistance.\nQuand il se transforme en [spell=768], son mana se change alors en énergie, pouvant [spell=5215] tout en se déplaçant, d’augmenter parfois ça vitesse de courses de 70% et de bondir derrière ces ennemis pour attaquer avec le talent [spell=49376]. C’est une forme orienté vers les dégâts de mêlée en faisant saigner leur cible avec [spell=49800] ou [spell=62078] lorsque le druide est entouré d’ennemis.\nAvec les talents de druide équilibre, la [spell=24858] est réputé pour faire beaucoup de dégâts à distance notamment avec les sorts [spell=5176] et [spell=48505] qui peuvent être augmenté avec des points de talent. Il émet aussi une aura, qui augmente les coups critiques des sorts, très appréciée en raid.\nSa forme d’[spell=33891] (talent restauration) est conçue pour soigner sur la durée notamment avec les sorts [spell=33763] et [spell=48438]. Il émet une aura, qui augmente les soins de 6%. Il a la particularité d’avoir une grande régénération de mana.\n\nD’autres formes animales secondaires complètent cette liste : sa [spell=783] qui permet au druide d’augmenter sa vitesse de déplacement, sa [spell=1066] qui lui permet de respirer sous l’eau tout en nageant plus vite et sa [spell=33943] (et avec la compétence [spell=34091], la [spell=40120]) lui permet de voler instantanément.\n\n[ul]\n[li] Dans l’arbre de talent Combat farouche, les druides ont une aura [spell=17007] très utile pour tout groupe de raid.[/li]\n[li] Le sort [spell=20484] est utilisable en combat, mais à une recharge de 10 min.[/li]\n[li] Il possède le sort [spell=29166] qui lui permet de régénérer le mana très vite même en combat, sur lui ou tout autre membre.[/li]\n[li] Les Druides ont leur propre capacité de téléportation qui leur permet de voyager vers [zone=493], ce qui est utile lorsqu’ils ont besoin de s’entraîner.[/li]'),(13,9,2,NULL,0,2,'[b][color=c9]Les Démonistes[/color][/b], vêtue d’armure légère, sont les maîtres des arts démoniaques. Ils possèdent des capacités très puissantes qui, si elles sont utilisées correctement, en font un adversaire formidable. Utilisant leurs malédictions en combinaison avec des sorts de dégâts directs, il cause des ravages et la destruction. Ses caractéristiques principales sont la puissance des sorts et l’intelligence.\n\nLes Démonistes qui ont choisi de se spécialiser dans l’arbre de talent Affliction, excellent dans l’utilisation des malédictions, ils posent sur leurs ennemis [spell=47865] pour les affaiblir ou [spell=47864] pour leurs faire des dégâts. Ils ont la [spell=18271] ce qui augmente les dégâts des sorts d’ombre de 25%.\nLe démonologue appel des démons pour l’aider dans ces combats, il emploie principalement l’[spell=30146]. Il peut aussi se [spell=59672] en démon pour augmenter ses dégâts durant une courte période.\nLe Démonistes destruction utilise des sorts de feu tels que [spell=5740] ou [spell=17962] pour infliger d’importants dégâts directs.\n\nLes Démonistes, tout en étant d’excellent dans les dégâts à distance, soutiennent beaucoup leurs alliés en appelant d’autre joueur avec [spell=698] ou en utilisant des magies rituelles pour conjurer des pierres imbues du pouvoir de guérir : [icon name=inv_stone_04][url=item=5509]Pierre de soin[/url][/icon].\n\n[ul]\n[li] Le démoniste est doté du sort [spell=1454] qui lui permet de sacrifier des points de vie pour régénérer son mana.[/li]\n[li] Le [spell=48020] lui permet une grande mobilité en annulant tous les effets de déplacement, et en s\'éloignant du corps-à-corps.[/li]\n[li] En utilisant le sort [spell=20022], le démoniste permet à la personne sur qui elle a été appliqué de ressusciter.[/li]\n[/ul]\n\n[b]Montures de classe :[/b]\n[ul]\n[li] [spell=5784], leurs yeux ne brûlent plus que d\'une haine inextinguible pour les démonistes qui les ont corrompus - Niveau 20 - Bonus de Vitesse de 60%. [/li]\n[li] [spell=23161] sont des destriers recréés qui ont été corrompus par les énergies infernales, transpirant et soufflant le feu - Niveau 60 - Bonus de vitesse : 100%. [/li]\n[/ul]'),(8,81,2,NULL,0,2,'[b]Les Pitons du Tonnerre[/b] est la faction de la capitale des Taurens : [zone=1638], située dans la partie nord de la région de [zone=215]. L\'ensemble de la ville est construit sur des falaises à plusieurs centaines de pieds au-dessus du paysage environnant, elle est accessible par des ascenseurs sur les côtés sud-ouest et nord-est.\n\n[h3]Histoire[/h3]\n\nLa grande ville de Pitons du Tonnerre se trouve au sommet d\'une série de mesas qui donnent sur les prairies verdoyantes de Mulgore. Les Taurens, autrefois nomade, ont récemment construit la ville pour dresser un centre de caravanes commerciales avec des artisans itinérants et des artisans de toutes sortes. Elle a été établi par le puissant chef [npc=3057] après que les Taurens, avec l\'aide des Orcs, ont chassé les centaures qui habitaient à l\'origine Mulgore. De longs ponts de corde et de bois font la liaison entre les mesas qui sont surmontées de tentes, de longues maisons, de totems peints aux couleurs vives et de huttes spirituelles. Le chef de Tauren surveille la ville animée, en veillant à ce que les tribus unies de Tauren vivent en paix et en sécurité.\n\n[h3]Réputation[/h3]\n\n[npc=14728] offre une quête répétitive où il faut fournir des étoffes. Une fois exalté aux Pitons du Tonnerre, en récompense, les joueurs peuvent acheter des [url=?items=15.5&filter=na=Kodo;cr=93:92;crs=2:1;crv=0:0]kodos[/url].'),(8,1038,2,NULL,0,2,'[b]Ogri\'la[/b] est un groupe d\'Ogres localisé dans [zone=3522], où leur proximité avec [item=32572] leur a permis d\'évoluer au-delà de leur nature brutale. Ils sont particulièrement impliqué dans une guerre contre le Dragon noir et la Légion ardente, qui cherchent les cristaux Apogides pour leurs propres fins.\n\n[h3]Localisation[/h3]\nOgri\'la est situé près du bord ouest des Tranchantes, entre le Camp de Forge: Terreur et le Camp de Forge: Courroux, juste à l\'ouest de Sylvanaar. Ogri\'la est seulement accessible en monture volante ou en forme de vol. Une autre alternative est d\'avoir une réputation d\'honoré ou plus élevé avec [faction=1031]. Mais un joueur doit avoir une monture volante pour atteindre le camp Garde Ciel près de Skettis.[pad]\n\n[h3]Reputation[/h3]\nLa reputation avec Ogri\'la ne peut être acquise que par quêtes, et il n\'y a que des quêtes répétables dont les [url=?quests&filter=da=ja;cr=1;crs=1038;crv=0]quêtes journalières[/url]. Il ya un plafond sur la quantité de réputation que l\'on peut obtenir chaque jour pour un joueur avec Ogri\'la, ce qui en fait une réputation \"difficile à farmer\".\n\n[b]Eclats Apogides[/b]\n[item=32569] peuvent être collectées de diverses manières. Ils peuvent être pillés sur le cadavres de monstres, recueillis à partir de l\'environnement, ou ils peuvent être en récompenses de quêtes terminées.[pad]\n[b]Cristaux Apogies[/b]\n[item=32572] se ramassent sur les élites de type Demons ou Dragons dans les Tranchantes. Pour appeler ces mobs, 35 Eclats Apogides sont nécessaires, et il est recommandé que vous ayez un groupe de 5 personnes pour les vaincre.\n\n[b]Quêtes[/b]\nIl y a un certain [url=?quests&filter=cr=1;crs=1038;crv=0]nombre de quêtes[/url] qu\'un joueur peut faire pour gagner de la réputation avec Ogri\'la, ainsi que plusieurs [url=?quests&filter=da=ja;cr=1;crs=1038;crv=0]quêtes quotidiennes[/url]. Beaucoup de quêtes quotidiennes seront également accordée à la réputation de la Garde Ciel Sha\'tari lorsqu\'elles seront complétées. \n\nPour accéder aux principales quêtes d\'Ogri\'la, un joueur doit d\'abord compléter les 5 quêtes de groupe de [npc=22941].\n\n[h3]Éléments épuisés[/h3]\nUn certain nombre d\'éléments apogides tombent parfois de mobs une fois mort. Lorsque vous avez amassé 50 éclats apogides, [url=?search=Apexis+Crystal+Infusion]les objets suivants peuvent être améliorés[/url], obtenant des statistiques supplémentaires et des emplacements de gemmes. Une fois ces objets améliorés, ils deviendront liés si équipés, et peuvent donc être vendus ou échangés avec d\'autres joueurs. Une chose à noter cependant, bien que les éléments épuisés peuvent également avoir des statistiques ou des effets, ils ne peuvent pas être équipés.'),(8,911,2,NULL,0,2,'[b]Lune d\'Argent[/b] est la capitale des elfes de sang, située dans la partie nord-est de [zone=3430] dans le royaume de Quel\'Thalas. La capitale,des elfes de sang, est à couper le souffle. Elle peut rivaliser avec la capitale naine de [zone=1537], capitale la plus ancienne du monde toujours debout. Récemment reconstruite, la ville abrite la plus grande population d\'elfes de sang en Azeroth. \n\nAujourd\'hui, Lune d\'Argent n\'est que la moitié orientale de la ville d\'origine. La moitié occidentale a été presque entièrement détruite par le fléau pendant la troisième guerre. La place de lÉpervier, est la seule partie occidental de Lune d\'Argent restant sous le contrôle des elfes de sang. La Malebrèche, chemin parcouru par Arthas Menethil et son armée de morts-vivants parties en quête de ressusciter Kel\'Thuzad, traverse tout le Bois des Chants éternels. Il sépare la Lune d\'Argent reconstruite et ces ruines de la moitié occidentale. Fait intéressant, les ruines de Lune d\'Argent ne logent pas de morts-vivants, au lieu de cela, elles contiennent des [url=?npcs&filter=cr=37;crs=6;crv=1502;na=Déshérité;maxle=8]déshérités[/url] et des [npc=15638]. Dans l\'état actuel des choses, Lune d\'Argent est encore la plus grandes des villes Hordeuses.\n\n[h3]Histoire[/h3]\n\nLa ville de Lune d\'Argent a été fondée par les hauts élus après leur arrivée à Lordaeron, il y a des milliers d\'années. La ville a été construite en pierre blanche autour de plantes vivantes dans le style de l\'ancien Empire Kaldorei. La ville contenait les célèbres académies de Lune d\'Argent, centre d\'apprentissage de la magie arcane, et la Flèche de Solfurie, majestueux palais abritant la famille royale des hauts-elfes. Également basé dans la ville, la convocation de Lune d\'Argent, également connu sous le nom de « Le Concile de Lune d\'Argent », était l\'organe dirigeant des hauts-elfes. À travers une étendue d\'océan vers le nord, il y a l\'île qui contient le plateau du puits du Soleil.\n\nBien que Lune d\'Argent ait resorti relativement indemne de la deuxième guerre, dans la troisième guerre, le Chevalier de la mort Arthas a mené le Fléau dans la ville, l\'attaquant au cours de sa quête pour atteindre le puit du Soleil. Le roi High Elven a été tué et la majorité de la population a été exterminée. Les forces de fléau ont tenu la ville pendant un certain temps mais l\'ont abandonné après l\'épuisement de ses ressources. \n\nBien que la ville ait été attaquée par le Fléau, elle n\'est pas aussi détruite qu\'on pourrait le penser. Beaucoup de ses plantes sont mortes, quelques cadavres sont étendu sur le pavé, la ville était à l\'abri du feu et de la destruction. Lune d\'Argent ressemble maintenant à une ville fantôme, intacte, mais étrangement abandonnée. Néanmoins, les chasseurs de trésors fréquentent fréquemment les ruines de Lune d\'Argent pour essayer de trouver certains des artefacts précieux que les elfes ont laissés derrière avant de déserter la ville, mais les fantômes des anciens habitants de Lune d\'Argent les en empêchent.\n\n[h3]Réputation[/h3]\n\n[npc=20612] offre une quête répétitive où il faut fournir des étoffes. Une fois exalté à Lune d\'Argent, en récompense, les joueurs peuvent acheter des [url=?items=15.5&filter=cr=151;crs=6;crv=35513;na=Faucon-pérégrin]Faucon-pérégrins[/url].\n\nLes zones environnantes du Bois des Chants éternels et des terres fantômes contiennent la plupart des quêtes pour gagner de la réputation avec Lune d\'Argent.'),(8,577,2,NULL,0,2,'[minibox]\n[h2]Cartel Gentepression[/h2]\n[faction=21]\n[b]Long-guet[/b]\n[faction=369]\n[faction=470]\n[/Minibox]\n\n[b]Long-guet[/b], faction de la ville du même nom, est un poste commercial dirigé par les gobelins du Cartel Gentepression. Il se trouve au carrefour des principales routes commerciales du [zone=618].\n\n[h3]Histoire[/h3]\n\nCette ville est le dernier point de la civilisation avant d\'atteindre le Mont Hyjal. Il est géré par les gobelins comme un poste commercial. La ville est officiellement neutre pour toutes les races et factions. Seuls les pèlerins peuvent monter jusquà lArbre-Monde, point culminant du Mont Hyjal. Long-guet est donc la destination la plus haute que les marchands et les aventuriers peuvent atteindre sans l\'autorisation des Elfes de nuit. Elle offrirait une vue dominante sur Kalimdor, si les nuages qui enveloppent continuellement les flancs de la montagne, disparaissaient.\n\nLong-guet est le seul avant-poste de gobelin majeur dans le nord de Kalimdor. Tout d\'abord, il sert de base aux opérations pour les mineurs de thorium et d\'arcanites puisque le Berceau-de-lHiver possède quelques veines inexploitées de ces matériaux. Deuxièmement, il sert de centre d\'échanges entre l\'Alliance et la Horde. Alors que Long-guet est à peine plus sûr que Reflet-de-Lune, généralement, l\'Alliance et la Horde se traitent assez bien là-bas. En outre, Long-guet est un point d\'arrêt et de réapprovisionnement fréquent pour les fidèles qui font le pèlerinage du Berceau-de-lHiver au Mont Hyjal.\n\n[h3]Réputation[/h3]\n\nLa réputation de Long-guet et du Cartel Gentepressin provient surtout des quêtes du Berceau-de-lHiver. Avec une réputation au minimum amicale, les gardiens vous aident en cas dattaque initiée contre vous.'),(8,21,2,NULL,0,2,'[minibox]\n[h2]Cartel Gentepression[/h2]\n[b]Baie-du-Butin[/b]\n[faction=577]\n[faction=369]\n[faction=470]\n[/minibox]\n\n\n[b]Baie-du-Butin[/b] est une grande ville pirate nichée dans les falaises entourant un magnifique lagon bleu, à lextrémité de [zone=33]. Pour entrer dans la ville, il faut passer au travers les mâchoires blanchis d\'un requin géant.\n\nParcouru par les Écumeurs des Flots noirs qui sont étroitement associés eu Cartel Gentepression, le port offre des opportunités à n\'importe quel voyageur passant par là, indépendamment de leur faction. Combiné à la célèbre « taverne du Loup de mer », le [event=15], de nombreux maîtres de profession et des vendeurs, qui vendent de tout (des animaux de compagnie aux anneaux de diamant), c\'est l\'un des endroits les plus populaires en Azeroth.\n\n[npc=2496], chef de la ville, embauche toute l\'aide qu\'il peut obtenir contre [faction=87] et autres menaces de la ville. Il réside avec le chef des Écumeurs des Flots noirs, [npc=2487], au sommet de l\'auberge de Baie-du-Butin.\n\nEn raison de la liaison par bateau de Baie-du-Butin à Cabestan, les joueurs de tout niveau (surtout de la Horde, si le niveau est faible) peut-être croisés dans le port, bien que les visiteurs les plus fréquents seront dans les niveaux 35-45, car les quêtes disponibles auprès des gens du pays se situent dans cette tranche de niveau.\n\nL\'eau est parsemée de débris flottants et de bancs de poissons. Plusieurs types de poissons se pèchent dans les eaux de la Baie, tels que le [item=6359], le [item=6358], et l\'[item=13422]. La pêche, dans les débris flottants, vous donnera également plus de chance de pêcher des coffres et d\'autres articles, faisant de Baie-du-Butin un endroit idéal pour la pêche.\n\n[h3]Réputation[/h3]\nLa plupart des quêtes pour augmenter la réputation avec Baie-du-Butin sont situés au Cap de Strangleronce. Avec une réputation au minimum amicale, les gardes vous aiderons en cas dattaque contre vous.\n\nSi vous êtes haï avec Baie-du-butin vous pouvez faire la quête répétable [quest=9259] pour revenir à Neutre.'),(8,470,2,NULL,0,2,'[minibox]\n[h2]Cartel Gentepression[/h2]\n[faction=21]\n[faction=577]\n[faction=369]\n[b]Cabestan[/b]\n[/Minibox]\n\n[b]Cabestan[/b], faction de la ville du même nom, situé sur la côte est de Kalimdor dans [zone=17]. Elle est dirigée par des gobelins. Ses rues se répandent dans toutes les directions, et l\'architecture ne montre aucune cohérence ni vision commune. C\'est une ville de divertissement et de commerce, où tout ce que vous voudriez acheter est en vente mais aussi beaucoup de chose que personne ne veut jamais. \n\nCabestan est actuellement géré par un groupe d\'entreprises connu sous le nom du Cartel Gentepression, un groupe fragmenté de la KapitalRisk, qui a d\'abord construit la ville portuaire pour la négociation avec [zone=1637]. C\'est d\'abord une faction neutre où Horde et Alliance se côtoient. Un bateau relie commodément Cabestan à Baie-du-butin.\n\n[h3]Histoire[/h3]\n\nConstruit à part égales entre l\'industrie et de la décadence, la ville portuaire gobeline de Cabestan s\'étend sur près d\'un kilomètre de littoral des Tarides de l\'est, entre [zone=14] et [zone=15]. Cabestan est la fierté des gobelins, une ville commerciale où vous pouvez trouver presque tout ce que votre cur désire, et si quelque chose n\'est pas en stock, vous pouvez parier que les gobelins peuvent le commander. Cabestan est desservie régulièrement par les bateaux qui font la traversé en passant devant la forteresse de Theramore, vers le sud.\n\nCabestan est une ville où les habitants, qui étaient autrefois des truands, règnent maintenant. Ses rues errent sans rime ni raison à travers des quartiers dédiés à une seule activité : le commerce. Des entrepôts délabrés se situent à côté de maisons en pierre majestueuses. Les belles boutiques sont voisines avec des cabanes grossières. Des objets de toutes les formes, et certains au-delà de l\'imagination, sont exposés sur les marchés et les boutiques exclusives.\n\nLes Gobelins accueillent toutes personnes ayant de l\'or, des éléments de valeur et une volonté de les échanger contre leurs marchandises et leurs services. Les marchands traversent la ville tous les jours, vendent tout, de la soie aux esclaves. Même la nuit, les magasins qui bordent les rues et les allées restent ouverts aux entreprises. Ceux qui ont de l\'argent peuvent écouter des musiciens qualifiés, tout en buvant des bières fines et en mangeant des aliments préparés par des grands chefs. Pour ceux qui ont des goûts plus terriens, on retrouve le long des quais des marchants d\'armes, la banque et des casinos.\n\nCabestan est le plus grand port de Kalimdor, beaucoup de navires transportant de la cargaison sortent pour d\'autres sites autour de Kalimdor. En plus des navires commerciaux légitimes, les bâtiments pirates reçoivent une amnistie dans le port de Cabestan tant qu\'ils peuvent payer des droits d\'accostage rigides. Cette situation rend les capitaines marchands furieux, mais ils ne peuvent boycotter Cabestan, sinon c\'est la faillite pour leurs commerces. En outre, les avocats et les mercenaires qui rôdent sur le front de mer sont impatients de faire face à tous ceux qui cherchent à causer des problèmes.\n\n[h3]Réputation[/h3]\n\nLa plupart des quêtes pour élever la réputation avec Cabestan et le Cartel Gentepression sont situées dans les Tarides. Avoir une réputation au minimum amicale, les gardiens aident en cas d\'attaque contre vous.\n\nSi vous êtes détesté auprès de Cabestan, vous pouvez faire la quête répétable [quest=9267] pour revenir à une réputation Neutre.'),(8,369,2,NULL,0,2,'[minibox]\n[h2]Cartel Gentepression[/h2]\n[faction=21]\n[faction=577]\n[b]Gadgetzan[/b]\n[faction=470]\n[/minibox]\n\n[b]Gadgetzan[/b] est la faction de la ville du même nom, qui abrite les plus grands ingénieurs, alchimistes et marchands gobelins. Seul endroit de civilisation au nord du désert de [zone=440], elle est perçue comme une oasis. Gadgetzan est le siège du Cartel Gentepression, le plus grand cartel gobelin. Les gobelins croient au profit plus quà la loyauté, donc Gadgetzan est considéré comme territoire Neutre dans le conflit Horde / Alliance.\n\n[h3]Histoire[/h3]\n\nBien que la neutralité des gobelins soit presque universellement reconnue, il y a encore ceux qui cherchent à semer le chaos et lanarchie. Pour Gadgetzan, cela vient sous la forme des bandits Bat-le-désert, une bande de mécréants qui occupe le champ des Puisatiers et les ruines d\'Ombre-du-Zénith au Nord-est de Tanaris. Peu de Gobelins se soucient des ruines antiques (à moins quils y aient un trésor), les bandits peuvent avoir les vieux blocs de pierre. \nCependant, le champ des Puisatiers est vital pour la survie des gobelins, leur fournissant lor liquide du désert. Les tours d\'eau dans le champ ont été construites sous la chaleur ardente du soleil, par le travail de leurs esclaves. Les gobelins ne vont pas abandonner leurs tours durement gagnées, aussi facilement. Mais, ils doivent rester en ville pour arrêter le conflit, en apparence interminable, parmi les différents visiteurs et donc empêcher de perturber les affaires. Par conséquent, ils embauchent de braves mercenaires venant de tous les coins du monde pour les aider.\n\n[h3]Réputation[/h3]\n\nEn tuant les [url=?npcs=7&filter=na=mers+du+Sud]Flibustiers des mers du Sud[/url] et les [url=?npcs=7&filter=na=bat-le-désert]Bandits Bat-le-désert[/url], la réputation avec le cartel Gentepression augmentera. Ayant une réputation au minimum amicale, les gardes vous aideront en cas d\'attaque contre vous. Avoir une réputation exaltée signifie que les gardes ne vous attaqueront jamais même si vous lancez des attaques sur la faction opposée. \n\nLa plupart des quêtes associées à la faction Gadgetzan sont situées à Tanaris. \n\nSi vous êtes détestés avec Gadgetzan, vous pouvez faire la quête répétable [quest=9268] pour obtenir la Neutralité.'),(8,47,2,NULL,0,2,'[b]Forgefer[/b] est la faction associée à la capitale des nains, [zone=1537]. [npc=2784] règle son royaume de Khaz Modan de sa salle du trône dans la ville, et [npc=7937], chef des gnomes, a temporairement dû s\'établir dans Brikabrok après la récente chute de la ville gnome [zone=133].\n\n[h3]Histoire[/h3]\n\nForgefer est l\'ancienne demeure des nains, une merveille façonnée dans la pierre. Forgefer a été construite au cur même des montagnes, une ville souterraine qui abrite des explorateurs, des mineurs et des guerriers. Les portes massives de roche protègent la ville en temps de guerre, et la lave de la montagne est redirigée et distribuée à des fins de chaleur, d\'énergie et de forage. \nAvant que le clan de Sombrefer ne soit banni de la ville, menant à la Guerre des Trois Marteaux, Forgefer était le centre commercial et social de tous les clans nains. Il appartient maintenant au Clan Barbe-de-bronze. \nBeaucoup de bastions nains ont chuté pendant la Guerre de Lordaeron, entre la Horde et l\'Alliance, mais la puissante ville de Forgefer, nichée dans les sommets hivernaux de [zone=1] et protégée par ses grandes portes, n\'a jamais été violée par la Horde envahissante.\n\nRelativement récemment, Forgefer est également devenu le foyer des Exilés de Gnomeregan. Après la troisième guerre, la ville gnome fut envahie par Troggs. Depuis lors, un certain nombre de gnomes se sont installés à Forgefer, transformant une zone de cette ville à leur goût, une région connue sous le nom de Brikabrok.\n\nForgefer est l\'une des villes les plus peuplées du monde, venant après la ville humaine de [zone=1519], et abritant 20 000 personnes.\n\nAlors que l\'Alliance a été affaiblie par les événements récents, les nains de Forgefer, dirigés par le roi Magni Barbe-de-bronze, forment un nouveau futur dans le monde. \n\n[h3]Réputation[/h3]\n\n[npc=14723] offre une quête répétitive où il faut fournir des étoffes. Une fois exalté à Forgefer, en récompense, les joueurs peuvent acheter des [url=?items=15.5&filter=cr=93:92:151:151;crs=2:1:6:6;crv=0:0:33977:33976;na=bélier] béliers [/url].\n\nLes zones environnantes [zone=1], [zone=38] et [zone=11] contiennent la plupart des quêtes pour gagner de la réputation auprès de Forgefer.'),(8,54,2,NULL,0,2,'[b]Les Exilés de Gnomeregan[/b] est la faction des gnomes qui ont fui leur domicile, [zone=133] à [zone=1]. Elle a été détruite par [url=?npcs=7&filter=na=Trogg] les Troggs[/url] après une invasion toxique. Maintenant, membre de lalliance, la plupart sont situés à Brikabrok, une partie de la ville voisine [zone=1537], y compris le leader [npc=7937].\n\n[h3]Histoire[/h3]\n\nOn a spéculé que les gnomes ont été formés comme des robots par les titans, en raison de leur nature curieuse et de leurs compétences techniques. Ils vivaient autrefois dans la cité de Gnomeregan, sans doute la plus belle ville technologique du monde.\n\nLes gnomes étaient une race souterraine de bricoleurs, jusquà ce que les Troggs aient détruit Gnomeregan. Dans cette guerre, plus de 80% de la population gnome a été exterminé.\n\n[h3]Réputation[/h3]\n\n[npc=14724] offre une quêtes répétables où il faut fournir des étoffes. En étant exalté aux Exilés de Gnomeregan, les joueurs sont capables de conduire des [url=?items=15.5&filter=cr=93:92;crs=2:1;crv=0:0;na=mécanotrotteur]mécanotrotteurs[/url].\n[zone=1] contient la plupart des quêtes pour gagner la réputation avec les exiés de Gnomeregan.'),(8,72,2,NULL,0,2,'[b]Hurlevent[/b] est la faction associée à [zone=1519], la capitale des Humains. Elle est située dans la partie nord-ouest de la [zone=12]. L\'enfant roi, [npc=1747], réside dans le Donjon de Hurlevent, entouré de ses gardes du corps et de ses conseillers, [npc=1748] (le régent) et [npc=1749]. La ville est nommée ainsi à cause des rafales soudaines et occasionnelles créées par la forme spéciale des montagnes autour de la ville glorieuse.\n\n[h3]Histoire[/h3]\n\nPendant la Première Guerre, le Royaume d\'Azeroth, y compris sa capitale, le Donjon de Hurlevent, a été complètement détruit par la Horde. Ses survivants ont fui vers Lordaeron. Après que les orcs ont été vaincus, au Portail des Ténèbres, à la fin de la Deuxième Guerre, il a été décidé que la ville serait reconstruite, dépassant sa grandeur dantan. Des tailleurs de pierres et des architectes ont pu été rassemblés par les nobles de Hurlevent. Sous la directio de cette équipe, la plus qualifiée et la plus ingénieuse, Hurlevent a été reconstruit dans une période de temps incroyablement courte. Maintenant, à la fin de la troisième guerre, dans le renommé Royaume de Hurlevent. Cest l\'un des derniers bastions du pouvoir humain laissé dans le monde.\n\nAvec la chute des Royaumes du Nord, Hurlevent est de loin la ville la plus peuplée du monde. Avec une population de deux cents mille personnes (principalement humaines), elle sert à bien des égards comme le centre culturel et commercial de l\'Alliance, même avec un accès à la mer. Les humains qui vivent dans la ville sont généralement insouciants et artistiques, favorisant les vêtements légers et colorés, la cuisine et l\'art. Elle abrite l\'Académie des sciences arcanes, la seule école de sorcellerie dans les royaumes de l\'Est, ainsi que le SI:7, une organisation de renseignement.\n\nCependant, les gens de Hurlevent ont du mal à accepter le rôle de Theramore en tant que foyer de la nouvelle Alliance. Ils sont convaincus que Hurlevent devrait être l\'héritière légitime du rôle de la ville de Lordaeron comme par le passé, mais aussi que Theramore est attristé face à l\'aggravation de la situation au sein de Les Royaumes de l\'Est.\n\n[h3]Réputation[/h3]\n\n[npc=14722] propose une quête répétable pour obtenir une réputation plus élevée avec Hurlevent. En contrepartie d\'une réputation exaltée, les joueurs non-humains peuvent monter sur des chevaux.\n\nLa plupart des quêtes associées à Hurlevent viennent des zones environnantes de la forêt d\'Elwynn, [zone=40] et [zone=44].'),(8,930,2,NULL,0,2,'[b]Exodar[/b] est la faction associée à [zone=3557], la capitale enchantée des Draeneï construit avec la plus grande partie de leur vaisseau qui sest écrasé. Il est situé dans la partie ouest de l[zone=3524]. Le chef de la faction Exodar est [npc=17468], qui est situé près des maîtres de combat dans la Voûte des Lumières.\n\n[h3]Histoire[/h3]\n\nLes Draeneï rescapés du crash de leur vaisseau se sont récemment réveillés pour reconstruire lExodar, encore fumant de limpact. L\'Exodar était autrefois une structure de satellite naaru autour de la forteresse dimensionnelle du [url=?search=donjon+tempête]Donjon de la Tempête[/url]. L\'Exodar contient une grande quantité de merveilles technologiques (en raison de ses origines avec le Donjon), comme des «fils» magiquement enchantés qui transmettent de l\'énergie sainte dans tout le navire pour alimenter le chauffage et l\'éclairage, tout en augmentant les pouvoirs, déjà considérable, des Draeneï.\n\n[h3]Réputation[/h3]\n\nComme pour les autres grandes factions associées aux races principales, la réputation de l\'Exodar peut être acquise en faisant la quête répétable de [npc=20604] [small][/small], ou alors, en tuant la faction adverse dans [zone=2597] (les elfes de sang) et en faisant les quêtes appropriées. Avec la réputation, le joueur peut acheter des objets provenant de fournisseurs liés à Exodar pour 10% de moins et, une fois exalté, le joueur peut acheter [url=?Items=15.5&filter=na=elekk;cr=93:92;Crs=2:1;crv=0:0] diverses montures[/url].'),(8,69,2,NULL,0,2,'[b]Darnassus[/b] est la faction de la ville de [zone=1657], la capitale des Elfes de la nuit. La haute prêtresse, [npc=7999], réside dans le Temples de la Lune, entourée d\'autres surs d\'Elune. Dans l\'Enclave Cénarien, l\'[npc=3516] conduit le [faction=609], souvent en opposition directe avec ses autres druides à [zone=493] et Tyrande elle-même.\n\n[h3]Histoire[/h3]\n\nAu lendemain de la troisième guerre, les Elfes de la nuit devaient s\'adapter à leur existence mortelle. Un tel ajustement était loin d\'être facile. Beaucoup d\'Elfes de la nuit ne pouvaient pas s\'adapter aux perspectives de vieillissement, de maladie et de fragilité. En cherchant à retrouver leur immortalité, un certain nombre de druides capricieux conspiraient pour planter un arbre spécial qui rétablirait un lien entre leurs esprits et le monde éternel.\n\nAvec [npc=15362] disparu, Fandral Forteramure, le chef de la conspiration qui souhaitaient planter le nouvel Arbre-Monde, est devenu le nouvel Archidruide. En un rien de temps, lui et ses camarades druides ont pris les devants et ont planté le grand arbre, [zone=141], au large des côtes orageuses du nord de Kalimdor. Avec leur soin, l\'arbre a poussé au-dessus des nuages. Parmi les branches crépusculaires de l\'arbre colossal, la merveilleuse ville de Darnassus a pris racine. Cependant, l\'arbre n\'a pas été béni par la nature et s\'avère être corrompu par la Légion Ardente. Maintenant, la faune et même les membres de Teldrassil sont contaminés par une obscurité croissante.\n\n[h3]Réputation[/h3]\n\n[npc=14725] offre une quête répétable [quest=7800] utilisé par les joueurs de l\'Alliance pour obtenir le droit de monter des [url=?items=15.5&filter=cr=93:92:151;crs=2:1:6;crv=0:0:13086;na=sabre;si=-1]Sabres-de-nuit[/url]. Les joueurs qui sont au minimum niveau 44, cherchant à gagner la faveur de Darnassus, devraient trouver et compléter les quêtes de [zone=357]. Les quêtes sont associées à Darnassus et pourraient accroître considérablement votre réputation.'),(8,809,2,NULL,0,2,'Les [b]Shen\'dralar[/b] sont la faction des Elfes de nuit restant dans [zone=2557]. Ils sont un groupe qui pratique la magie arcane à son apogée sur les traces de leur ancienne reine Azshara, et de ses partisans, les Bien-nées. Ils vivent à Eldre\'Thalas (nom antérieur de Hache-tripes) depuis la fin de la guerre des Anciens. Ils sont peu nombreux, mais leur connaissance et leur pouvoir mystique sont géniaux.\n\nLeur chef, [npc=11486], était chargé de superviser la construction des pylônes pour contenir le grand démon [npc=11496] et absorber son pouvoir démoniaque. Après de longues et nombreuses années, le pouvoir des pylônes a commencé à diminuer, le prince a entrepris de tuer les elfes de nuit restants pour maintenir l\'énergie. Les esprits des défunts demandent vengeance, mais seuls des aventuriers aguerris peuvent le tuer. Faite-vite, il reste très peu d\'habitants en vie.\n\n[h3]Réputation[/h3]\n\nLa réputation peut être obtenue en rendant à plusieurs reprises les quêtes obtenus avec les trois Librams de Hache-Tripes : [item=18333], [item=18334] et [item=18332]. \nLa réputation peut être obtenue aussi via les livres de classe suivant :\n[ul] \n[li] [item=18357] - Guerrier [/li] \n[li] [item=18363] - Chaman [/li] \n[li] [item=18356] - Voleur [/li] \n[li] [item=18360] - Démoniste [/li] \n[li] [item=18362] - Prêtre [/li]\n[li] [item=18358] - Mage [/li]\n[li] [item=18364] - Druide [/li]\n[li] [item=18361] - Chasseur [/li]\n[li] [item=18359] - Paladin [/li]\n[li] [item=18401] - Guerrier et Paladin [/li] \n[/ul] \nLes livres de classe et les librams donnent 500 points de réputation chacun.'),(8,349,2,NULL,0,2,'[b]Ravenholdt[/b] est une guilde de voleurs et d\'assassins qui ne reçoit que ceux d\'une extraordinaire prouesse. Ils sont opposés à la [faction=70]. La quête, [quest=8249], est disponible pour les classes non-voleurs, mais elle nécessite l\'aide d\'un voleur pour obtenir les objets pour la quête. Le manoir de Ravenholdt, le siège de la faction, est situé dans [zone=36], mais pour y arriver, vous devez venir du coin nord-est de [zone=267].\n\n[h3]Réputation[/h3]\n\nTous les [url=?Search=Syndicat#npcs]membres du Syndicat [/url] donnent 1-5 points de réputation en fonction de votre niveau actuel. De plus, il existe quelques quêtes qui augmentent votre réputation, mais la méthode principale pour élever votre réputation provient des quêtes répétées pour fournir les objets demandés.\n\nVous commencez à une réputation Neutre (0/3000) avec Ravenholdt, ce qui signifie que si vous tuez un NPC de Ravenholdt avant d\'augmenter votre réputation d\'au moins 5, vous deviendrez hostile et ne pourrez jamais augmenter votre réputation. \nPour augmenter votre réputation de Neutre à Amicale, la quête répétable [quest=6701] est disponible. Vous devrez fournir 11-12 [item=17124] et une fois que vous êtes amical, cette quête n\'est plus disponible. Vous pouvez également fournir cinq [item=16885].\nPour augmenter votre réputation au-delà de Amical, le seul choix est la quête répétable, [quest=8249]. \n\n[h3]Récompense[/h3]\n\nIl n\'y a aucune récompense de faction connue pour obtenir que se soit avec une réputation Amicale, un honoré, révéré ou exalté, sauf que les gardes vous parlent avec plus de respect. \n\nCependant, La réputation Exalté est nécessaire pour obtenir le Haut-Fait : [achievement=2336].'),(8,87,2,NULL,0,2,'Les [b] Pirates de la Voile Sanglante [/b] semblent être l\'une de ces organisations, qui sont apparues en Azeroth pendant les événements menant à la troisième guerre et à la suite de la troisième guerre. Ils sont originaires du Rivage Cruel, où leur chef, l\'[npc=2546], organise les opérations. Ils ont maintenant l\'intention de paralyser et de piller la ville portuaire de [faction=21], contrôlée par le Cartel Gentepression et sous la protection des Ecumeurs des Flots noirs. Il est probable que les Pirates de le Voile Sanglante sont venus profiter de la perte actuelle de leur flotte, sur la côte de la [zone=45], dans laquelle deux de ses navires ont été détruits. Le navire restant a été obligé de trouver un abri dans une crique où son équipe lutte maintenant pour survivre aux escarmouches des Nagas.\n\nEn préparation de l\'attaque, les Pirates de la Voile Sanglante ont pris position dans des endroits clés près de la ville. À l\'heure actuelle, ils ont trois navires ancrés le long du littoral au sud de Baie-du-Butin, à l\'abri des canons défensifs de la ville. Des camps ont également été construits le long de la même côte en prévision de l\'attaque. En outre, une fête scoute a atterri juste à l\'ouest de l\'entrée de la ville, signalant toutes les activités, ainsi qu\'un camp construit le long de la route menant vers la ville, susceptible d\'empêcher tout renfort.\n\nLes Pirates de la Voile Sanglante cherchent à atteindre leurs objectifs sans avoir leurs forces engagées dans la bataille, à cette fin, chaque côté cherche maintenant l\'aide d\'aventuriers sympathiques à leur cause.\n\n[h3]Réputation [/h3]\n\nIl n\'y a qu\'une seule façon d\'augmenter votre réputation auprès des Pirates de la Voile Sanglante et c\'est de libérer votre colère contre tous les citoyens de Baie-du-Butin. Voici une liste de tous les citoyens de Baie-du-Butin et leur valeur de réputation. \n[ul]\n[li] [npc=4624] : 25 points de réputation gagné [/li]\n[li] [npc=15088] : 25 points de réputation gagné [/li]\n[li] [npc=2496] : 5 points de réputation gagné [/li]\n[li] [npc=2636] : 5 points de réputation gagné [/li]\n[li] [url=?Npcs&filter=cr=3;crs=21;crv=0] Plusieurs autres NPC [/url][/Li]\n[/Ul]\nLe montant gagné avec les Pirates de la Voile Sanglante est indiqué pour un niveau 60 non humain. Le montant perdu pour tuer un citoyen ne peut pas être démontré car il dépend de votre niveau actuel avec Baie-du-Butin et de l\'importance de la personne que vous tuez. En plus de cela, quand vous perdez de la réputation avec Baie-du-Butin, vous perdez la moitié dans les trois autres villes du Cartel Gentepression. Par exemple, si vous perdez 25 points avec Baie-du-Butin, vous perdrez 12,5 points avec [faction=470].\n\nLe moyen le plus rapide d\'augmenter votre réputation avec les Pirates de la Voile Sanglante est de tuer des habitants de Baie-du-Butin. Au début, cela peut sembler une tâche simple car les gardes n\'apparaissent pas aussi menaçants que les autres monstres auxquels un joueur est confronté dans le jeu. Cependant, les gardes sont très équipés pour neutraliser les joueurs de toute classe, afin d\'éviter que les gens ne s\'attaquent les uns les autres dans la ville. \n\nLe Cogneur de Baie-du-butin a l\'avantage avec plusieurs capacités. Lune dentre elle est lutilisation de filet pour vous bloquer sur place, vous empêchant de vous échapper. Une autre est le fait qu\'ils appellent dautres Cogneurs chaque fois que vous attaquiez un citoyen de la ville ou si vous êtes sous un statut hostile avec Baie-du-Butin, les joueurs peuvent bientôt se retrouver rapidement submergés par les Cogneurs.\nLa capacité la plus forte du Cogneur est quune fois qu\'il tire son arme, il est peu probable que vous vivez, si vous ne vous échappez pas assez vite. Chaque fois qu\'un Cogneur vous tire dessus, l\'attaque vous retient, tout comme une attaque de marteau d\'Ogre. La différence ici, est que le Cogneur peut tirer rapidement en succession, provoquant des lances de chaîne. Un joueur peut littéralement être jeté d\'un côté de la ville à l\'autre, ce qui vous empêche d\'attaquer. Plus souvent, vous vous retrouverez coincé dans un coin, incapable de bouger et incapable d\'attaquer avec tous les sortilèges interrompues par l\'attaque du Cogneur. Parce que les Cogneurs ne rangent pas leurs armes à feu une fois qu\'elles sont sorties, la meilleure façon d\'agir est de s\'enfuir.\n\nPar essais et erreurs, la plupart des gens ont découvert un endroit sûr pour tuer les Cogneurs de Baie-du-Butin. Si vous suivez le tunnel qui mène à la ville, le chemin de votre gauche qui mène à la maison du Forgeron est l\'endroit idéal pour tuer les gardes. Seuls deux gardes patrouillent sur ce chemin. Une fois qu\'ils sont partis, entrer dans la première construction sur le chemin pour provoquer un rassemblement. Un joueur devrait pouvoir tuer 2 à 4 Cogneurs avant que les deux Cogneurs de patrouille en appellent dautres. En moyenne, un joueur qui fait cela peut tuer environ 30 à 40 Cogneurs de Baie-du-Butin, gagnant environ 800 points de réputation auprès de la Voile Sanglante. Les Cogneurs ici ne semblent pas sortir leurs armes, mais si vous vous trouvez dans une mauvaise situation, vous pouvez sauter sur la balustrade, courir sur le chemin des eaux, pour vous échapper.\n\nPour augmenter votre réputation au-delà de honoré, seuls deux NPC vous le permettent : \n[ul]\n[li] [npc=9179] : 5 points de réputation toutes les 7 minutes jusquà révéré [/li]\n[li] [npc=26081]: 5 points de réputation toutes les 24 heures jusquà exalté [/li]\n[/Ul]\n\n[h3]Récompenses[/h3]\n\nDevenir amical avec Les Pirates de la Voile Sanglante, vous donnera accès aux éléments suivants :\n[ul]\n[li] [item=12185] - Invoque un [npc=11236] [/li]\n[li] [item=22742] [/li]\n[li] [item=22743] [/li]\n[li] [item=22745] [/li]\n[/Ul]\nVous aurez besoin d\'être honoré avec la Voile Sanglante pour [achievement=2336].'),(8,70,2,NULL,0,2,'Le[b] Syndicat [/b] est une organisation criminelle humaine qui opère principalement dans les [zone=45] et les [zone=36], bien que quelques petits campements soient éparpillés dans les [zone=267]. Leur effectif compte environ 3 000 personnes.\n\nIls ont trois chefs : [npc=2423], descendant du premier Lord d\'Alterac, qui dirige les actions du Syndicat dans les montagnes Alterac, [npc=2597] dirige les actions du Syndicat dans les Hautes Terres d\'Arathi à partir de la principale demeure, le Donjon semi-abandonnée de Stromgarde, et Lady Beve Perenolde, fille d\'Aiden Perenolde.\n\n[h3]Histoire[/h3]\n\nPendant la seconde guerre, Lord Perenolde qui dirige le royaume d\'Alterac, a été découvert pour être en liaison avec les orcs de la Horde. Perenolde croyait qu\'une victoire de le Horde était inévitable et offrait ainsi une aide à la Horde en suscitant des rébellions, en attaquant les bases de l\'Alliance et en leur fournissant des armes. Lorsque cette trahison fut découverte, l\'Alliance marchait contre Alterac et la détruisit. Perenolde et tous les nobles qui ont accompagné ses projets ont été dépouillés de leurs titres et de leurs terres. Beaucoup d\'entre eux ont réussi à s\'échapper, mais ont commencé à comploter pour se venger. En utilisant leur fortune encore considérables, la noblesse a engagé une bande de voleurs et d\'assassins, formant une organisation connue sous le nom de Syndicat.\n\nAu début, le but du Syndicat était simplement de répandre le chaos et le désordre, frappant des bases cachées dans les montagnes d\'Alterac. Avec la fin de la troisième guerre et le chaos qui suivie, les dirigeants du Syndicat ont vu leur chance de reprendre Alterac et de retrouver leurs anciens pouvoirs. Ils ont maintenant pris le contrôle de plusieurs avant-postes dans la région environnante, y compris le donjon abandonnée et une partie de la ville de Stromgarde.\n\nIls sont haïe par l\'Alliance, qu\'ils considèrent comme leurs ennemis mortels, et la Horde, qu\'ils considèrent comme des brutes faits pour travailler en esclaves. En conséquence, le Syndicat est maintenant chassé par les deux factions, avec [npc=10181], en particulier, une prime est sur sa tête, tous les membres du Syndicat capturés seront exécutés sommairement. En outre, [npc=4949] a commandé un certain nombre de ses agents, y compris [npc=2229], [npc=2239], [npc=2238] et leur chef [npc=2316] pour lancer une enquête sur la nature du Syndicate et ses activités, ainsi que pour récupérer [item=3498], un collier maintenant porté par Elysa, la maîtresse de Lord Aliden, qui appartenait à un son cher ami, [npc=18887].\n\n[h3]Réputation[/h3]\n\nLe Syndicat, en tant que faction dans World of Warcraft, est très étrange par rapport à la plupart des factions. En effet, que le meurtre des membres de cette faction ne réduira pas votre réputation. Pour la plupart des joueurs, qui ne sont pas voleur, la seule façon d\'afficher le Syndicat dans leur menu de réputation est de compléter la quête [quest=8249]. Cependant, la quête requiert [item=16885] ... que seuls les voleurs peuvent obtenir en volant à la tir des PNJ au-dessus du niveau cinquante ce qui rend difficile d\'organiser une telle transaction.\n\nActuellement, il n\'y a qu\'une seule option connue pour augmenter la réputation d\'un joueur avec le Syndicat, en tuant des membres de la faction [faction=349]. Il n\'y a pas de récompenses connues pour avoir augmenté la réputation du Syndicat. Les PNJ affiliés à Ravenholdt ne donnent que 1 point de réputation, à l\'exception de [npc=13085], qui donne 5 (bien que la perte de réputation correspondante avec Ravenholdt soit aussi cinq fois plus grande ). Tous les joueurs commençent à une réputation détestée de 32000/36000, il faudrait tuer 10 000 PNJ de Ravenholdt pour atteindre le statut neutre avec la faction. Malheureusement, l\'état neutre est le plus élevé que vous puissiez atteindre avec le Syndicat, ce n\'est pas pour dissuader les joueurs, aucun des NPC Ravenholdt ne grimpe la réputation.\n\n[b]AVERTISSEMENT[/b]: Si vous décidez de tuer les PNJ de Ravenholdt, sachez qu\'il n\'y a actuellement aucun moyen de restaurer votre positionnement avec Ravenholdt, si vous passez en dessous de Neutre. La raison du problème est qu\'aucune des quêtes qui donnent des points de réputation de Ravenholdt ne sera disponible car aucun des membres de Ravenholdt ne vous parleront. Cela signifierait qu\'il s\'agit d\'un changement permanent et que vous ne pourrez plus jamais interagir avec l\'un des NPC fidèles à Ravenholdt. Notez également que les joueurs commencent à la réputation de 0/3000 avec Ravenholdt, et le fait de tuer même un de leurs PNJ à ce niveau de réputation vous empêchera pour toujours de rétablir votre réputation avec eux.'),(8,59,2,NULL,0,2,'[b]La Confrérie du Thorium[/b] est un groupe d\'artisans d\'élite qui vend un certain nombre de recettes épiques, par contre, vous devez obtenir suffisamment de réputation avec eux. Tous les joueurs commencent à la réputation : Neutre.\n\n[h3]Histoire[/h3]\n\nLa [zone=51] abrite un groupe de nains exceptionnellement robustes qui se sont séparés du Clan Sombrefer. Sur les falaises surplombant la région appelée « Le Chaudron », dans le grand nord des Gorges des vents brulants, les nains de la Confrérie du Thorium ont établi une base d\'opérations, la Halte du Thorium. De là, ils surveillent de près les activités des nains de Sombrefer dans les Gorges des vents brûlants. Les aventuriers qui cherchent la Halte du Thorium trouveront que les nains de la Confrérie du Thorium qui donnent de grandes récompenses pour ceux qui les aident dans leur lutte sans fin contre leurs anciens frères.\n\nLa Confrérie du Thorium comprend de nombreux artisans exceptionnellement talentueux, et les forgerons de la Confrérie sont censés être parmi les meilleurs Azeroth. Ils possèdent les connaissances requises pour fabriquer les armes et les armures de [npc=11502], le Seigneur du Feu, mais n\'ont pas de main-d\'uvre pour obtenir les matériaux nécessaires à l\'artisanat. On raconte qu\'un membre de la Confrérie du Thorium a été habilité à échanger les recettes et les projets fabuleux des nains avec ceux qui peuvent prouver leur fidélité à la Confrérie. Bien sûr, pour prouver sa fidélité, l\'aventurier doit s\'aventurer au coeur de [zone=2717], le domaine de Ragnaros, le Seigneur du Feu lui-même, pour fournir aux nains les matières premières rares trouvées là-bas. Une tâche ardue, sans aucun doute, mais avoir accès aux secrets de la Confrérie du Thorium devrait s\'avérer être une récompense qui vaut bien l\'effort.\n\n[h3]Réputation[/h3]\n\n[b]De Neutre à Amical[/b]\n[ul]\n[li] Fournir : [item=18944], [item=3857] et [item=4234], [item=3575] ou [item=3356] au [npc=14624]. [/Li]\n[/ul]\n[b]De Amical à Honoré[/b]\n[ul]\n[li] Fournir : [item=18945] au [npc=14624]. [/Li] \n[/ul]\n[b]De Honoré à Exalté[/b]\n[ul]\n[li] Fournir : [item=11370] à [npc=12944]. [/Li]\n[li] Fournir : [item=17012] à Lokhtos Sombrescompte. [/Li]\n[li] Fournir : [item=17010] à Lokhtos Sombrescompte. [/Li]\n[li] Fournir : [item=17011] à Lokhtos Sombrescompte. [/Li]\n[li] Fournir : [item=11382] à Lokhtos Sombrescompte. [/Li] \n[/ul]'),(8,68,2,NULL,0,2,'[b]Fossoyeuse[/b] est la faction pour la capitale du même nom, [zone=1497], régie par Sylvanas Coursevent. La cité est situé dans la [zone=85], au bord nord des Royaumes de l\'Est. La ville proprement dite est sous les ruines de la ville historique de Lordaeron. Pour y entrer, vous traverserez les défenses extérieures en ruines de Lordaeron et la salle du trône abandonnée, jusqu\'à ce que vous atteigniez l\'un des trois ascenseurs gardés par deux abominations.\n\n[h3]Histoire[/h3]\n\nFossoyeuse était à l\'origine un système d\'égouts, de cryptes et de catacombes sous la capitale de Lordaeron. Après que la ville a été détruite par le Fléau, Arthas a reconstruit et agrandit le dédale de souterrain. Initialement, il voulait que Fossoyeuse soit son siège de pouvoir, d\'où il gouvernerait les terres de pestes. Cependant, peu de temps après la fin de la troisième guerre, Arthas a été obligé de retourner à Norfendre et de sauver le Roi Liche. En son absence, [npc=10181] et ses non-morts rebelles ont capturé les ruines de la ville. Peu de temps après, elle a découvert la grande forteresse souterraine et a décidé de l\'établir comme base principale des opérations pour les Réprouvés.\n\n[h3]Réputation[/h3]\n\n[npc=14729] offre une quête répétitive où il faut fournir des étoffes. Une fois exalté à Fossoyeuse, en récompense, les joueurs peuvent acheter des [url=?items=15.5&filter=cr=93:92;crs=2:1;crv=0:0;na=squelette] chevaux squelettiques [/url].\n\nLes zones environnantes [zone=267], [zone=130], et la [zone=85] contiennent la plupart des quêtes pour gagner de la réputation auprès de Fossoyeuse.'),(8,909,2,NULL,0,2,'La [b]Foire de Sombrelune[/b] est un mystérieux carnaval itinérant, qui parcourt non seulement Azeroth, mais aussi lOutreterre. Conduite par l\'inimitable [npc=14823], un gnome d\'héritage douteux et de racine inconnue. La Foire amène des jeux, des prix et des bibelots exotiques inattendus, puissants ou non, en [zone=215], à la [zone=12] ou à la [zone=3519] chaque mois.\n\nUne variété de divertissement est proposée par la Foire, mais l\'attraction la plus commune est la rédaction du billet. Plusieurs forains distribuent des [item=19182], répartis dans toute la Foire, ils offrent des bons contre des articles fabriqués par des travailleurs du cuir, des forgerons ou des ingénieurs ainsi que des objets rassemblés dans la nature tels que [item=11404] et [item=19933]. Les bons peuvent être échangés contre de nombreuses choses allant de la [item=19295] à des colliers de grande puissance.\n\nBeaucoup d\'aventuriers recherchent la Foire de Sombrelune pour trouver les mystiques [url=?items=15.0&filter=minle=1;cr=107;crs=0;crv=Combine+the+Ace]carte de Sombrelune[/url]. Les cartes de Sombrelune viennent en huit combinaisons, chacune ayant une suite de l\'As aux Huit. Avec la combinaison de toutes les cartes, la suite est créée qui commencera une quête pour vous envoyer à la foire de Sombrelune. \nChacune des huit suites produit un [url=?items=4.-4&filter=na=carte+sombrelune] bijou [/url] différent avec un effet différent, dont certains sont assez puissants.\n\nLe calendrier habituel de la Foire de Sombrelune arrive sur le site, le premier vendredi du mois et le départ commencera tôt le lundi suivant.'),(8,76,2,NULL,0,2,'[b]Orgrimmar[/b] est la faction de la capital des orcs : [zone=1637]. Situé au bord nord de [zone=14], la ville imposante abrite le chef de guerre orcs, [npc=4949].\n\n[h3]Histoire[/h3]\n\nThrall a dirigé les orcs vers le continent de Kalimdor, où ils ont fondé une nouvelle patrie avec l\'aide de leurs frères tauren. En nommant leur nouvelle terre, Durotar, nom du père assassiné de Thrall, les orcs se sont installés pour reconstruire leur société autrefois glorieuse. La malédiction démoniaque sur leur race a pris fin, la Horde a décidé de passer dun discours de conquête avec une coalition lâche à la survie et à la prospérité pour tous. Aidé par les nobles Taurens et les Trolls rusés de la tribu Sombrelance, Thrall et ses orcs attendaient une nouvelle ère de paix dans leur propre pays.\n\nDe là, ils ont commencé la création de la grande ville guerrière, Orgrimmar. Nommé de l\'ancien chef de guerre, Orgrim [color=#ff143c]Doomhammer[/color], la nouvelle ville a été construite en peu de temps, à l\'aide des gobelins, des Taurens, des trolls et de [color=#ff122a]Mok\'Nathal Rexxar[/color]. En dépit d\'avoir des problèmes avec les centaures, les harpies, les lézards de tonnerre enragés, les kobolds, et malheureusement, l\'Alliance, Orgrimmar a prospéré et est devenu le foyer des orcs et des Trolls Sombrelance.\n\nAujourd\'hui, Orgrimmar se trouve à la base d\'une montagne entre Durotar et [zone=16]. Une ville guerrière en effet, elle abrite d\'innombrables quantités d\'Orcs, Trolls, Taurens, et une quantité croissante de Réprouvés rejoignent maintenant la ville, ainsi que les Elfes de Sang qui ont récemment été acceptés dans la Horde.\n\n[h3]Réputation[/h3]\n\n[npc=14726] offre une quête répétitive où il faut fournir des étoffes. Une fois exalté à Orgrimmar, en récompense, les joueurs peuvent acheter des[url=?items=15.5&filter=cr=93:92;crs=2:1;crv=0:0;na=Loup] loups [/url].\n\nLes zones environnantes Durotar et [zone=17] contiennent la plupart des quêtes pour gagner de la réputation avec Orgrimmar.'),(8,530,2,NULL,0,2,'[b]Les Trolls Sombrelances[/b], tribu de Trolls exilés, ont uni leurs forces avec [npc=4949] et la Horde. Ils appellent maintenant [zone=1637] leur maison, qu\'ils partagent avec leurs alliés Orc. [npc=10540] est leur chef actuel.\n\n[h3]Histoire [/h3]\n\nLorsque les rivalités tribales ont éclaté dans l\'ancien Empire Gurubashi, la tribu Sombrelance s\'est trouvée chassée de sa patrie dans [zone=33]. S\'étant installés dans ce que l\'on croit aujourd\'hui être les îles brisées, la tribu se retrouve bientôt enchevêtrée dans un conflit avec une bande de murlocs. Leur sort semblait scellé jusqu\'à ce que Thrall, chef de guerre Orc, et son armée, nouvellement libérés, s\'emparent de leurs maisons. Contrôlée par une sorcière des mers, un groupe de murlocs a capturé le chef des Sombrelances, Sen\'jin, avec Thrall et plusieurs autres Orcs et Trolls. Thrall a réussi à se libérer avec d\'autres, mais n\'a finalement pas pu sauver le chef des Trolls. Bien que Sen\'jin ait été sacrifié par la sorcière des mers, il a pu révéler une vision qu\'il avait eu, dans laquelle Thrall conduirait les Sombrelances hors des îles.\n\nAprès son retour, Thrall et ses partisans ont réussi à repousser de nouvelles attaques de la sorcière des mers et de ses murlocs, et se sont à nouveau dirigés vers Kalimdor. Sous la direction de [npc=10540], les Sombrelances ont alors juré allégeance à la Horde de Thrall et les ont suivi. Maintenant considérés comme ennemis par toutes les autres tribus Trolls sauf les Vengebroches et les Zandalar, les Sombrelances sont aujourd\'hui méprisés. Pourtant, les Trolls Sombrelances n\'ont pas oublié quils ont été chassés de leurs terres ancestrales et cette animosité gardée est accentuée avec limpatience, surtout vers les autres tribus Trolls. Après avoir atteint la nouvelle patrie des Orcs, [zone=14], les trolls se sont alors installés sur les rives orientales du royaume Orc, les îles Echo.\n\nCependant, avec l\'arrivée de Kul Tiras et de sa marine, les Sombrelances ont été forcés de reculer à l\'intérieur des terres sous l\'assaut du commandant. Les Trolls, se battant avec la Horde aux côtés de leurs frères, ont vaincu l\'ennemi. Les Trolls ont alors réclamé leur nouvelle patrie. Peu de temps après, un sorcier du nom de [npc=3205] a commencé à utiliser la magie noire pour prendre possession de ses collègues Sombrelances. Au fur et à mesure que son armée de disciples augmentait, Vol\'jin ordonna que les trolls restant évacuent, alors Zalazane prit le contrôle des îles Echo. Les Sombrelances se sont installés sur la rive voisine, en nommant leur nouveau village en hommage à leur ancien chef Sen\'jin. Du village de Sen\'jin, ils envoient, avec leurs alliés, des forces pour combattre Zalazane et son armée asservie.\n\n[h3]Réputation[/h3]\n\n[npc=14727] offre une quête répétitive où il faut fournir des étoffes. Une fois exalté aux Trolls Sombrelances, en récompense, les joueurs peuvent acheter des [url=?items=15.5&filter=na=Raptor;cr=93:92;crs=2:1;crv=0:0] Raptors [/url].\nLa zone environnante, Durotar, contient la plupart des quêtes pour gagner de la réputation avec les Trolls Sombrelances. De plus, les joueurs de niveau supérieur ont également une bonne quantité de quêtes dans [zone=3521].'),(8,92,2,NULL,0,2,'[b]Les Gelkis[/b] sont une tribu de centaures qui ont construit leur campement dans les parties les plus au sud de [zone=405]. Ce sont les ennemis mortels des [faction=93], une tribu de frère située également dans le sud de Desolace. Le chef fondateur, ou Khan, des Gelkis était [npc=13741], deuxième de la prétendue progéniture de Zaetar et Theradras. Ils sont actuellement dirigés par [npc=5602] et ont pour représentant [npc=5397].\nLes Gelkis ne tiennent aucune alliance avec leurs tribus de frères, mais sont aussi connus pour agir à la fois hostilement et passivement envers les membres de l\'Alliance comme de la Horde.\n\n[h3]Histoire[/h3]\n\nInitialement dirigé par le Second Khan Gelk, les Gelkis se situaient dans les régions les plus au sud de Desolace lorsque la tribu centaure se divisa en cinq.\nLorsque la tribu Gelkis s\'est prononcée contre le Khan Magra, une éternelle querelle entre les Magram et les Gelkis est née.\n\nLes Gelkis considérés comme plus civilisés que leurs frères avec une structure sociale organisée et une compréhension ferme de la langue commune, respectent la nature et leur mère de naissance Theradras. \nAlors que les Magram prônent la force comme essentielle et que la survie de la tribu dépend de leur esprit de combat.\n\nPour alléger ce conflit, Theradras veille toujours sur les centaures et gardera les tribus en sécurité et en vie. Les Gelkis ont alors demandé sa protection et donc le pouvoir de la terre maintien leur existence. \n\nBien que la Magram considère que cela soit faible, il semblerait que ce soit une vue erronée, car des élémentaires peuvent être aperçu dans Village Gelkis, mettant un terme aux intrus indésirables aux côtés de leurs maîtres centaures.\n\n[h3]Réputation[/h3]\n\nCest une des deux factions situées en Desolace, vous devez avoir une certaine réputation auprès des Gelkis pour commencer leurs quêtes. La réputation pour les Gelkis peut être obtenue en tuant les [url=?Npcs=7&filter=na=Magram]centaures Magram[/url].\n\nVous gagnez 20 points de réputation chez les Gelkis et perds 100 avec la tribu Magram.'),(8,93,2,NULL,0,2,'[b]Les Magram[/b] sont une tribu de centaures qui construit leur campement dans les parties sud-est de [zone=405]. Ce sont les ennemis mortels de la [faction=92], une tribu de frère située également dans le sud de Desolace. Le chef fondateur, ou Khan, des Magram était [npc=13740], troisième de la prétendue progéniture de Zaetar et Theradras. Ils sont actuellement dirigés par [npc=5601] et ont pour représentant [npc=5398].\nLes Magram ne tiennent aucune alliance avec leurs tribus de frères, mais osont aussi connus pour agir à la fois hostilement et passivement envers les membres de l\'Alliance comme de la Horde.\n\n[h3]Histoire[/h3]\n\nÀ l\'origine menée par le troisième Khan Magra, les Magram se situaient contre les chaînes de montagnes de Desolace lorsque la tribu centaure se divisa en cinq.\nAvant la mort de Magra, il a installé l\'idée que la force était essentielle et que la survie de la tribu dépendait de son esprit de combat. Quand leur frère, la tribu Gelkis, s\'est prononcée contre cette notion, une éternelle querelle entre les deux tribus est née.\n\nLa poursuite de la force a continué à travers les Khans Magram jusqu\'à ce jour, transformant les centaures en des êtres violents et déterminés. Pour solidifier leur titre de plus fort, la tribu lutte encore férocement pour affaiblir ou détruire leurs clans de frères, considérant les Kolkar comme faible, les Gelkis comme une nuisance, et les Maraudon comme un formidable ennemi.\n\nOn peut supposer que la culture Magram s\'est développée autour de la force de culte avant tout. Par rapport aux Gelkis, les Magram tiennent des formes très primitives de la parole et de la structure sociale. Par exemple, leur compréhension commune est limitée et la position de Khan serait vraisemblablement recherchée par un démon de la mort.\n\n[h3]Réputation[/h3]\n\nC\'est une des deux factions situées à Desolace, vous devez avoir une certaine réputation auprès des Magram pour commencer leurs quêtes. La réputation pour les Magram peut être obtenue en tuant [url=?npcs=7&filter=na=Gelkis]les centaures Gelkis[/url]. \n\nVous gagnez 20 points de réputation chez les Magram et perds 100 avec la tribu Gelkis.'),(8,270,2,NULL,0,2,'Les trolls de la[b] Tribu Zandalar[/b] sont venus à île de Yojamba dans la [zone=33] pour recruter de l\'aide contre le Dieu du sang ressuscité et ses prêtres d\'Atal\'ai dans [zone=19] et [zone=1417].\n\n[h3]Histoire[/h3]\n\nLes Zandalar étaient les premiers trolls connus, tribu d\'où provenaient toutes les tribus. Au fil du temps, deux empires troll distincts ont émergé, l\'Amani et le Gurubashi. Ils existaient pendant des milliers d\'années jusqu\'à l\'avènement des Elfes de la nuit, qui ont combattu avec eux et ont finalement conduit les deux empires à l\'exil.\n\nÀ la suite du Great Sundering, les Gurubashi vaincus sont de plus en plus désespérés. En cherchant un moyen de survivre, ils ont enrôlé l\'aide du sauvage [npc=14834], également appelé Soulflayer. Hakkar s\'est transformé en un oppresseur impitoyable qui a exigé des sacrifices quotidiens de ses sujets, les Gurubashi se sont alors retournés contre leur sombre maître. Les tribus les plus fortes (y compris les Zandalar) se sont regroupées pour vaincre Hakkar et ses fidèles prêtres, les Atal\'ai. Les tribus unies ont vaincu le Dieu des Sang et ont expulsé les Atal\'ai, et malgré leur victoire, l\'Empire Gurubashi tomba peu de temps après.\n\nAu cours des dernières années, les prêtres d\'Atal\'ai ont découvert que la forme physique de Hakkar ne peut être convoquée que dans la capitale ancienne et déserte de l\'Empire Gurubashi, Zul\'Gurub. Malheureusement, au cur de cette nouvelle quête, les prêtres ont invoqué, avec succès, Hakkar, confirmant la présence du Soulflayer redouté au cur des ruines.\n\nAinsi, la tribu Zandalar est arrivée sur les rives d\'Azeroth pour combattre encore Hakkar. Mais le dieu du sang est devenu de plus en plus puissant, pliant plusieurs tribus à sa volonté, et même, commandant les avatars des dieux primitifs: chauve-souris, panthère, tigre, araignée et serpent. Avec les tribus trolls éparpillées, les Zandalri ont été forcés de recruter des aventuriers de diverse origine d\'Azeroth pour les rejoindre dans la bataille, et espèrent une fois de plus vaincre, le Soulflayer.\n\n[h3]Réputation[/h3]\n\nLa réputation avec la tribu Zandalar est obtenue en tuant les monstres et boss dans Zul\'Gurub. Des quêtes répétitives et spécifiques sont aussi disponibles, elles requièrent des éléments qui ont été abandonnés dans linstance. Chaque Zul\'Gurub donne environ 2 500 à 3 000 de réputation.\nAvant la croisade brûlante, la principale raison de monter la réputation avec la tribu était les enchantements [url=?Items=0.6&filter=na=Zandalar]dépaule[/url], [url=?items=0.6&filter=minrl=60;maxrl=60;cr=18:107;crs=4:0;crv=0:to+a+leg+or+head+slot+item]de tête et de jambe[/url]. De plus, il y avait des pièces darmure en récompense de quête à faire dans Zul\'Gurub nécessitant un niveau de réputation.'),(8,471,2,NULL,0,2,'[b]Les Marteaux-hardis[/b] sont un clan de nains actuellement centrés dans [zone=47] et la [zone=3520]. La faction a été supprimée dans le patch 2.0.1.\n\n[h3]Histoire[/h3]\n\nJuste avant le [objet=175739], le clan Marteaux-hardis, dirigé par Thane Khardros Marteaux-hardis, habitait les contreforts et les falaises autour de Forgefer. Le clan Marteaux-hardis a échoué à prendre le contrôle de [zone=1537], des clans Barbe-de-bronze et Sombrefer. Khardros et ses guerriers Marteaux-hardis se sont rendus au nord par les barrières de Dun Algaz et ont fondé leur propre royaume dans le lointain sommet de Grim Batol. Là, les Marteaux-hardis ont prospéré et reconstruit leurs richesses.\n\n[npc=9019] et ses Sombrefer ont juré de se venger de Forgefer. Thaurissan et sa femme sorcière, Modgud, ont lancé un attentat contre Forgefer et Grim Batol. les forces de Modgud ont commencé à franchir les portes de Grim Batol, elle a utilisé ses pouvoirs pour frapper la peur dans leurs curs. Les ombres se déplaçaient à son commandement, et des choses sombres se glissaient dans les profondeurs de la terre pour traquer les Marteaux-hardis dans leurs propres retranchements. Finalement, Modgud a franchi les portes et a assiégé la forteresse elle-même. Les Marteaux-hardis se sont battus désespérément, Khardros lui-même sest lancé dans la bataille pour tuer la sorcière reine. Avec leur reine perdue, les Sombrefer ont fui avant la fureur des Marteaux-hardis.\n\nUne fois que la menace immédiate des Sombrefer a été éliminée, les Marteaux-hardis sont rentrés à Grim Batol. Cependant, la mort du Modgud avait laissé une tache maléfique sur la forteresse de la montagne, et les Marteaux-hardis la trouvaient inhabitable. Khardros a conduit son peuple vers le nord vers les terres de Lordaeron. En s\'installant dans la région montagneuse des Hinterlands, et ces forêts luxuriantes, les Marteaux-hardis ont construit la ville de Nid-de-laigle, où les Marteaux-hardis se sont rapprochés de la nature et même liés aux puissants griffons de la région.\n\nLa menace la plus immédiate pour leurs sécurités vient de l\'est sous la forme de deux clans trolls, les Vilebranches et les Fanécorces. Ils sont les plus célèbres pour organiser des batailles contre la ville des Marteaux-hardis, tout en brandissant des armes puissantes.\nLes nains Marteaux-hardis ont un certain nombre de clans, chacun gouverné par un Thane. Le plus fort Thane règne sur Nid-de-laigle.'),(8,509,2,NULL,0,2,'[b]La Ligue d\'Arathor[/b] a été initialement établie par les survivants du Royaume de Stromgarde pour récupérer la [zone=45] des mains des Profanateurs au Trépas d\'Orgrim. Aujourd\'hui, c\'est une organisation à l\'appui de l\'Alliance, basée sur [zone=3358] dans le Refuge de lOrnière. Ils se sont chargés d\'aider à fournir des forces, pour l\'Alliance, lorsque cest nécessaire, leurs membres incluent toutes les races de l\'Alliance mais se sont encore principalement des humains stromgardiens.\n\n[h3]Réputation[/h3]\n\nLes joueurs peuvent gagner la réputation dans cette faction en participant au champ de bataille du bassin Arathi. Lorsque vous vous battez dans le bassin d\'Arathi, vous gagnez 10 points de réputation pour 160 ressources. Sur les weekends d[event=20], les ressources requises sont ramenées à 150.\n\nOn vous accorde le titre, [title=48], une fois exalté avec Ligue dArathor et les deux autres factions du champ de bataille, [faction=890] et [faction=730].'),(8,730,2,NULL,0,2,'[b]Les Gardes Foudrepiques[/b] est la faction de l\'Alliance dans le champ de bataille [zone=2597]. Ils sont une expédition de nains du clan Foudrepique, originaire des « vallées d\'Alterac » dans [zone=36]. La recherche des Foudrepiques pour les reliques de leurs passés et la récolte de ressources dans la vallée d\'Alterac ont conduit à une guerre ouverte avec les Orcs de la [faction=729] habitant dans la partie sud de la vallée. Ils ont également reçu un « ordre de la souveraineté impérialiste » par [npc=2784] pour prendre les vallées d\'Alterac pour [zone=1537].\n\nLa principale base des Foudrepiques est Dun Baldar, où son chef, [npc=11948], réside avec ses maréchaux. Son second commandant, [npc=11949], se trouve au sud de Dun Baldar, à Cur de pierre.\n\n[h3]Réputation[/h3]\n\nLes joueurs peuvent gagner leurs réputation, dans cette faction, en participant au champ de bataille de la vallée dAlterac, en faisant diverses tâches et en tuant les membres de la faction adverse, le clan Frostwolf.\n\nOn vous accorde le titre : [title=48] au joueur, une fois quil est exalté avec les Gardes Foudrepiques et les deux autres factions des champs de bataille, [faction=890] et [faction=509].'),(8,510,2,NULL,0,2,'[b]Les Profanateurs[/b] cherchent à feuilleter la [faction=509] dans le champ de bataille, [zone=3358]. Aujourd\'hui, c\'est une organisation à l\'appui de la Horde, basée au Trépas dOrgrim dans [zone=45]. Ils se sont investis pour aider les forces de la Horde, au besoin, et leurs membres incluent toutes les races de la Horde, même si, se sont encore principalement des Orcs.\n\n[h3]Réputation[/h3]\n\nLes joueurs peuvent gagner la réputation dans cette faction en participant au champ de bataille du bassin Arathi. Lorsque vous vous battez dans le bassin d\'Arathi, vous gagnez 10 points de réputation pour 160 ressources. Sur les weekends d[event=20], les ressources requises sont ramenées à 150.\n\nOn vous accorde le titre, [title=48], une fois exalté avec les Profanateurs et les deux autres factions du champ de bataille, [faction=889] et [faction=729].'),(8,529,2,NULL,0,2,'L[b]Aube dArgent[/b] est une organisation axée sur la protection d\'Azeroth des menaces qui cherchent à la détruire, comme la Légion Ardente et le Fléau. Les forteresses de l\'Aube d\'Argent se trouvent dans les [zone=139] et les [zone=28]. Elle maintient également une présence dans [zone=1657] et dans les [zone=85], et dans dautres zones moins remarquables. La réputation avec lAube dArgent peut être utilisée pour acheter divers plans, consommables, et pour atténuer le coût à [zone=3456]. Avec l\'expansion « Burnning Croisade », la réputation de lAube dArgent a diminué en valeur.\n\nLe [item=22999] a pour icône un lever de soleil argenté.\n\n[h3]Histoire[/h3]\n\nAprès la mort du [npc=16062], la corruption de la Croisade Écarlate est devenu évidente pour certains de ses membres, qui ont par la suite abandonné les rangs de la [url=?npcs&filter=na=croisade%20écarlate;ex=on]Croisade Écarlate[/url] et a créé lAube dArgent pour protéger Azeroth de la menace du Fléau sans présence de fanatique dans la Croisade Écarlate.\n\nAlors qu\'ils partagent les mêmes objectifs que la Croisade, lAube dArgent a ouvert ses rangs non seulement aux races de l\'Alliance, mais aussi aux membres de la Horde et même à certains des Réprouvés. Ils mettent en garde contre la discrétion et l\'introspection, et mettent beaucoup l\'accent sur la recherche du Fléau et sur la façon de le combattre.\n\nAvec le temps, lAube dArgent s\'est diversifié, comme le Fléau qui s\'est divisé de nouveau, avec un rejeton appelé la Fraternité de la Lumière, un compromis entre l\'approche plus savante de lAube dArgent et le fanatisme de la Croisade écarlate.\n\n[h3] Réputation [/h3]\n\n[b]Les pierres du Fléau[/b]\nTout en portant un bijou accordant l\'effet « Commission pour lAube dArgent », les personnages peuvent tuer des monstres mort-vivants pour leurs [url=?items=12&filter=cr=151;crs=6;crv=43169;na=pierre%20du%20fléau] pierres du Fléau[/url] et ensuite les transformer en monnaies échange contre [item=12844]. Les quêtes requièrent beaucoup de [item=12843], [item=12841] et [item=12840]. Il convient de noter que les monnaies déchanges reçus des entités doivent être sauvegardés jusqu\'à ce que le statut de Révéré soit atteint, car les quêtes ne donneront plus de réputation après.\n\nUne autre façon daugmenter la réputation avec lAube dArgent est de faire la quête répétable « Chaudron ». Les chaudrons sont une source de « production » de membres du Fléau.\n\nComme la plupart des factions, le joueur peut faire des instances pour augmenter sa réputation. Les instances associées sont [zone=2017] et [zone=2057]. Naturellement, ces instances incluent également des quêtes qui augmentent la réputation de lAube dArgent.'),(8,933,2,NULL,0,2,'[b]Le Consortium[/b],dirigé par [npc=19674], sont des passeurs éthérés, des commerçants et des voleurs qui sont venus en Outreterre. Le principal base d\'opérations et le plus grand rassemblement se trouve à Foudreflèche, mais ils peuvent être trouvés à[color=#ff0537] Midrealm Post[/color], Aeris Landing, près d\'Auchindoun à [zone=3792] et dans d\'autres endroits.\n\nEn arrivant à un statut amical, les joueurs sont officiellement considérés comme membres du Consortium et bénéficient d\'un salaire. Le salaire est un sac de gemmes au début de chaque mois, donné par [npc=18265] chez Aeris Landing. Une plus grande réputation avec le Consortium produit des qualités et quantités supérieures de gemmes chaque mois.\n\n[h3]Réputation[/h3]\n\n[b]Jusqu\'à Amical[/b]\n[ul]\n[li]Faire le donjon Tombe-mana en [i]mode normal[/i] rapporte environs 1 200 points de réputation[/li]\n[li]Donner des [item=25416] à [npc=18265].[/li]\n[li]Donner des [item=25463] à [npc=18333].[/li]\n[/ul]\n\n[b]De amical à honoré[/b]\n[ul]\n[li]Faire Tombe-mana en [i]mode normal[/i] rapporte environs 1 200 point de réputation.[/li]\n[li]Activer les [item=25433] à [npc=18265].[/li]\n[li]Donner des [item=29209] à [npc=19880].[/li]\n[/ul]\n\n[b]De honoré à exalté[/b]\n[ul]\n[li]Faire Tombe-mana en [i]mode héroïque[/i] rapporte environs 2 400 points de réputation.[/li]\n[li]Faire toutes les [url=?Quêtes et filtre=cr=1;crs=933;crv=0]quêtes[/url].[/li]\n[li]Donner des [item=25433] à [npc=18265].[/li]\n[li]Donner des [item=29209] à [npc=19880].[/li]\n[/ul]\n\nToutes personnes qui essayent de gagner simultanément la réputation du Consortium et des [faction=941] ou [faction=978] peuvent se concentrer à tuer des ogres ([url=?npcs&filter=na=rochepoing;cr=6;crs=3518;crv=0]Rochepoing[/url], [url=?npcs&filter=na=cogneguerre;cr=6;crs=3518;crv=0]Cogneguerre[/url]) à Nagrand et rendre les perles de guerre obsidienne au Consortium.\n\nLa seule mise en garde est le taux de loot, soit environ 33% pour les Cogneguerre, alors qu\'il est de 50% pour les insignes. Si vous êtes au niveau 70 et que vous voulez monter cette réputation plus rapidement sans se soucier de la réputation de Mag\'har / Kurenai, vous voudrez peut-être donner des insignes à la place. Ensuite, les ogres sont généralement plus faciles à tuer, allant du niveau 65 à 67. Le choix dépend finalement du joueur.'),(8,932,2,NULL,0,2,'[b]L\'Aldor[/b] est un ancien ordre de prêtres draeneïs qui vénèrent les naaru, et à ce jour ils assistent les naaru [faction=935] dans leur combat contre [npc=22917] et la Légion Ardente. Ils se trouvent principalement dans la [zone=3520] et [zone=3703]. Bien qu\'ils aient beaucoup souffert des Elfes du sang qui sont devenus [faction=934], ont mis de côté une guerre ouverte contre les Sha\'tar. Le temple le plus saint de l\'Aldor repose sur léminence de l\'Aldor, surplombant la ville à l\'ouest.\n\nLa plupart des joueurs commenceront à une réputation neutre auprès de l\'Aldor. [npc=18166] à Shattrath donnera aux joueurs une première quête pour devenir amical avec Aldor ou Les clairvoyants. Ce choix est réversible si les joueurs ressentent le besoin.\nLes joueurs de Draenei seront directement amicaux avec Aldor et hostiles avec les Clairvoyants, alors que les joueurs Elfe du sang seront hostiles à l\'Aldor et amicaux envers les Clairvoyants.\n\n[npc=19321] et [npc=20807] sont situés dans la banque Aldor, sur le bord nord de la terrasse de la lumière. Le sanctuaire de la lumière sans fin sur léminence de l\'Aldor abrite [npc=20616] [petit][/small] et [npc=21906] [petit][/small], qui échangent, respectivement, des jetons épiques d\'armure contre des pièces de set de [url=?Itemsets&filter=ta=12]Niveau 4[/url] et de [url=?Itemsets&filter=ta=13]Niveau 5[/url].\n\n[i]Note : Les gains de réputation avec Aldor correspondent à une perte de réputation de 10% plus élevée chez les Clairvoyants. La plupart des gains de réputation avec Aldor accorderont également 50% de la réputation avec le Sha\'tar.[/i]\n\n[h3]Réputation[/h3]\n\n[b]Jusqu\'à honoré[/b]\n\nLes joueurs qui cherchent à gagner les rangs de réputation supérieurs (Révéré, Exalté) peuvent vouloir sauver des quêtes non répétables jusqu\'à ce qu\'ils soient honorés.\n\nDonner 10 [span class=q1][item=29425][/span] à [npc=18537] dans léminence de l\'Aldor accordera 250 points de réputation pour l\'Aldor. Il existe également une quête répétable où donner une unique marque accorde 25 points de réputation. Ces marques tombent sur des membres inférieurs de la Légion Ardente trouvés dans la plupart des zones de Outreterre, y compris les deux camps au nord d\'Auchindoun dans les déchets osseux de [zone=3519].\nEnviron 240 marques sont nécessaires pour passer d\'amical à honoré.\nEn outre, ces quêtes fournissent de la réputation de Sha\'tar ; 125 points de réputation pour 10 marques ou 12,5 points de réputation pour une unique marque.\n\nLes joueurs qui souhaitent également faire la réputation des factions [faction=978] ou [faction=941] iront tuer des Orcs à la forteresse de Kil\'Sorrow dans le sud-est de [zone=3518], car ils donnent des marques ainsi que 10 points de réputation auprès des Kurenai ou des Mag\'har.\n\n[b]Jusqu\'à Exalté[/b]\n\nUne fois que vous atteignez le niveau 68, vous pouvez également donner 10 [span class=q1] [item=30809][/span], c\'est le même principe que les marques de Kil\'jaeden mais ceux-ci tombent sur des partisans de haut rang de la Légion Ardente. Si vous le souhaitez, vous pouvez transformer les marques de niveau supérieur avant la réputation honorée. Dans [zone=3522], la porte de la mort dispose du plus grand nombre de membre avec ce grade.\n\n[b]Arme gangrenée[/b]\n\n[span class=q2][item=29740][/span] peut être donné à tout moment à [npc=18538] [small][/small] à léminence de l\'Aldor. Cela augmentera votre réputation avec l\'Aldor de 350 par arme gangrenée.\nEn plus des gains de réputation, vous recevrez [span class=q1][item=29735][/span], qui est la condition pour acheter lenchantement d\'épaule à [npc=20807] dans la banque de l\'Aldor.\n\n[h3]Passer à la réputation de l\'Aldor[/h3]\n\nPour changer votre faction des Claivoyants vers l\'Aldor et donc pour accéder à leurs recettes d\'artisanat (et annuler toutes les réputations que vous avez faites), trouvez [npc=18597], un membre de l\'Aldor dans la ville basse. Elle propose une quête répétable où pour 8x [span class=q1][item=25802][/span] vous montez la réputation Aldor. Une fois que vous êtes neutre, vous ne pourrez plus recevoir cette quête.'),(8,922,2,NULL,0,2,'[b]Tranquilliens[/b] a été reprise par les Réprouvés et les Elfes de sang puis est devenu une faction des [zone=3433].\n\n[h3]Histoire[/h3]\n\nAlors que l\'armée du Fléau faisait son chemin vers le Puit-du-Soleil, les elfes n\'avaient pas d\'autre choix que de se retirer, Tranquillien fût donc abandonnée. La ville est maintenant utilisée par les Elfes de sang et les Réprouvés comme base d\'opération pour lancer des attaques visant à reprendre les Terres Fantômes. Cependant, la ville est entourée par le fléau, même les courriers ont du mal à traverser l\'ennemi pour atteindre la ville. Les forces mortels de Mortholme sont la menace la plus dangereuse pour la ville.\n\n[h3]Réputation[/h3]\n\nContrairement à la plupart des zones de départ, la ville de Tranquillien a sa propre faction.\nToutes les quêtes que vous effectuez pour eux accumuleront au moins 1000 points de réputation. [npc=16528] agit comme lintendant des Tranquilliens. Vredigar peut être trouvé près de l\'auberge et vendra divers éléments [span class=q2]commun[/span], et même un manteau [span class=q3]rare[/span] lorsque vous atteignez la réputation exaltée.\n\nSi vous complétez toutes les quêtes des Tranquilliens, vous devriez être exalté.\nIl existe une variété de quêtes concernant principalement la récupération des villages envahis, l\'enquête sur les morts-vivants et l\'aide apportée à la population. La suite de quête prend « fin » avec la quête où il faut tuer [npc=16329].'),(8,910,2,NULL,0,2,'La [b]Progéniture de Nozdormu[/b] est une faction composée du vol Draconique de bronze. Leur chef, [npc=15192], se trouve à l\'extérieur des [b]Grottes du temps[/b], avec beaucoup de ses agents volant dans le ciel de [zone=1377].\n\nPour ouvrir les portes d[b]Ahn\'Qiraj[/b], un champion doit compléter une longue ligne de quête pour le dragon de bronze Anachronos. Cette réputation est également présente dans [zone=3428]; Elle permet dobtenir des équipements et des bagues épiques.\n\n[h3]Réputation [/h3]\n\nLes joueurs commencent leur réputation au plus bas niveau possible, cestà-dire 0/36000 de détestés.\n\nLa réputation de la Progéniture de Nozdormu peut être gagnée en tuant des monstres à l\'intérieur du temple d\'Ahn\'Qiraj et en faisant des quêtes liées. Vous pouvez également exploiter [item=20384], cela prend beaucoup plus de temps et nécessite l\'obtention de [item=20383] dans [zone=2677] pour la suite de quête [item=21175].\n\nTuer des monstres dans le temple d\'Ahn\'Qiraj ne permet que datteindre une réputation de 2999/3000 de neutre, la réputation ne peut donc être avancée que par des quêtes et la remise de [item=21229] et [item=21230]. \nUn conseil, gardez tous les insignes jusqu\'à ce que vous soyez à une réputation neutre, car à ce moment-là, cela devient beaucoup plus difficile.'),(8,749,2,NULL,0,2,'Les [b]Hydraxiens[/b] sont des élémentaires qui se sont installés sur les îles à l\'est de [zone=16]. Les ennemis jurés des armées de [npc=11502]. Historiquement serviteurs des Anciens Dieux, les quatre Lords Élémentaires ont servi les dieux avec une loyauté éternelle. Les minions de Neptulon, le chasse-marée, étaient nombreux et insensés. On ne sait pas encore comment le [npc=13278] a libéré le contrôle de son seigneur ou quels sont ses objectifs ultimes, mais les élémentaires deau sont les seuls éléments qui n\'attaquent pas les races mortelles.\n\nSitué sur une île éloignée dans l\'extrême est d\'Azshara, le Duke Hydraxis propose des quêtes. Les deux premiers nécessitent de tuer divers élémentaires dans les [zone=139] et en [zone=1377]. Une réputation accrue avec les Hydraxiens ouvre des quêtes supplémentaires menant à [zone=2717]. Tous les objets obtenus auprès des Hydraxiens sont gagnés à partir de différentes missions.\n\nL\'achèvement de la suite de quête permet aux joueurs d\'obtenir [item=17333] utilisé pour endommager les runes trouvées près de la plupart des boss dans Cur de Magma. Ceci est nécessaire pour convoquer [npc=12018], l\'avant-dernier boss, et, après sa défaite, pour convoquer Ragnaros lui-même. Comme il y a sept runes, tout raid nécessite au moins sept joueurs qui apportent une quintessence s\'ils souhaitent terminer l\'instance. Comme la majeure partie de la suite de quête a lieu au sein de Cur de Magma, toutes personnes du raid peuvent compléter cette tâche avec un peu plus que quelques voyages et une course au [zone=1583].\n\n[h3] Réputation [/h3]\n\nLa réputation des Hydraxiens est obtenue en tuant les ennemis élémentaires suivants :\n[ul][li] [npc=11746] - 5 points de réputation, jusqu\'à l\'Honoré. [/li]\n[li] [npc=11744] - 5 points de réputation, jusqu\'à Honoré.[/li]\n[li] [npc=7032] - 5 points de réputation, jusqu\'à Honoré.[/li]\n[li] [npc=9017] - 15 points de réputation, jusqu\'à Révéré. [/li]\n[li] [npc=14478] - 25 points de réputation, jusqu\'à Révéré. [/li]\n[li] [npc=9816] - 50 points de réputation, jusqu\'à Révéré. [/li]\n[li] [npc=11658], [npc=11673], [npc=12101] et [npc=11668] - 20 points de réputation, jusqu\'à Révéré. [/li]\n[li] [npc=11659], [npc=12100], [npc=12076], [npc=11667] et [npc=11666] - 40 points de réputation, jusqu\'à Révéré. [/li]\n[li] [npc=12118], [npc=11982], [npc=12259], [npc=12057], [npc=12056], [npc=12264] et [npc=12098] - 100 points de réputation, jusqu\'à Exalté. [/li]\n[li] [npc=11988] - 150 points de réputation, jusqu\'à Exalté. [/li]\n[li] [npc=11502] - 200 points de réputation, jusqu\'à Exalté. [/li][/ul]\n\nLa réputation au statut de Révéré avec les Hydraxiens permet aux joueurs dobtenir le [item=22754], qui se recharge. Et donc évite la nécessité de retourner à Hydraxis pour obtenir une nouvelle quintessence chaque semaine.'),(8,609,2,NULL,0,2,'Le [b]Cercle Cénarien [/b] est une organisation de druides, à la fois tauren et elfe de nuit, nommé d\'après Cénarius. Ses membres se consacrent à la protection de la nature et à la restauration de celle-ci suite aux dégâts subis par des forces malveillantes.\n\nLe Cercle a de nombreux sites, mais leur ville principale est la ville de Havre- nuit dans la [zone=493]. Les druides apprennent le sort [sortilège=18960] au niveau 10, mais il est aussi possible dy arriver par [zone=361] via le tunnel des Grumegueles.\n\nLe cercle Cénarien est aussi beaucoup présent en [zone=1377], où ils combattent les Silithides, les Qirajis et larmée du crépuscule. Le repos du vaillant et le Fort Cénarien servent de base dans ces terres hostiles et offrent de nombreuses opportunités aux aventuriers qui cherchent à aider les druides.\n\n[h3]Membres notables[/h3]\n\n[ul][li][npc=11832], fils de Cenarius [/li]\n[li][npc=3516], chef des druides - elfes de la nuit [/li]\n[li][npc=5769], chef des druides - Taurens [/li][/ul]\n\n[h3]Réputation[/h3]\n\nIl existe plusieurs façons de se faire connaître avec le cercle Cénarien.\nMise à part les [url=?Quests&filter=cr=1;crs=609;crv=0]quêtes[/url], vous pouvez faire ce qui suit pour gagner en réputation: \n[ul]\n[li]Le raid des [zone=3429] est de loin le moyen le plus rapide de gagner en réputation, car un clean complet peut dépasser 2000 points de réputation. [/li]\n[li] Tuez larmée du crépuscule. Elle cesse daugmenter une fois que vous atteignez la réputation Honoré pour [npc=11880] et [npc=11881], et Révéré pour [npc=15201].[/li]\n[li] Trouvez des [item=20404 ]. Ceux-ci se trouvent sur larmée du crépuscule et produisent 250 points de réputation pour 10 textes.[/li]\n[li] Trouvez des [item=20513], [item=20514] et [item=20515]. Ceux-ci se trouvent sur les mini-boss qui sont convoqués aux pierres de vent en utilisant [itemset=492]. [/li]\n[li] Effectuez la quête : [quest=8507]. Ce sont soit des [url=?search=logistique+Briefing] Quêtes de logistique [/url], des [url=?search=combat+Briefing]quêtes de Combat[/url] ou des [url=?search=tactique+Briefing] Quêtes tactiques [/url]. Les badges que vous gagnez de ces quêtes peuvent être transformés en réputation supplémentaire, si vous choisissez d\'abandonner les récompenses. [/li]\n[li] Collectez les [object=181598] de la zone et rendez les à votre faction.[/li]\n[/ul]'),(8,589,2,NULL,0,2,'Les [b]Éleveurs de sabres-d\'hiver[/b] est une faction de l\'Alliance composée de deux Elfes de la nuit qui peuvent être trouvés au [zone=618]. À l\'heure actuelle, le seul donneur de quête est [npc=10618], qui est situé au sommet du Rocher des Sabres-d\'hiver au Berceau-de-lhiver. En atteignant un niveau de réputation exalté avec cette faction, Rivern vendra une monture spéciale, le [item=13086].\n\nLa monture de cette faction est la seule monture épique, ayant une vitesse de 100%, utilisable avec une compétence en équitation de 75. La faction est connue pour ne pas avoir déquivalant côté Horde et être la plus longue et la plus répétitive des réputations à monter dans l\'ensemble du jeu. La première quête peut être faite au niveau 58, tandis que les deux autres sont réalisables quau niveau 60.\n\n[h3]Réputation[/h3]\n\nLa réputation avec les Éleveurs de sabres-d\'hiver ne peut être obtenue que par trois quêtes répétables. Il n\'y a pas d\'objets de faction ni de mobs qui récompensent la réputation directement.\n\n[b]De neutre 0 à 1500[/b]\n\nUne seule quête répétable sera disponible jusqu\'à ce quune réputation de 1500/3000 soit atteinte, la quête : [quest=4970] doit donc être répétée. Tous les [url=?npcs&filter=cr=6;crs=618;crv=0;na=Croc%20acéré]Ours[/url] et [url=?npcs&filter=cr=6;crs=618;crv=0;na=Noroît]Noroît[/url] au Berceau-de-lhivers peuvent looter les objets de quête. Cette quête doit être effectuée en solo, car les taux de loot sont faibles et ne sont pas partageables si d\'autres ont la quête.\n\n[b]De neutre 1500 à exalté [/b]\n\nÀ mi-chemin du neutre, la quête : [quest=5201] sera disponible. Cette quête nécessite de tuer 10 Tombe-hivers dans le village Tombe-hivers, juste à l\'est de Long-guet. Si la quête : [quest=8464] a été effectuée pour [faction=576], les [item=21383] peuvent tomber sur les Tombe-hivers. Si un joueur veut les deux réputations, il préférable quil les gardes jusquà ce quil soit Révéré avec les Grumegueules. Ce qui entraînera beaucoup de réputation \"gratuite\".\n\nCette quête peut se faire en groupes pour aller plus vite. Les joueurs qui augmentent les réputations des Éleveurs de sabres-d\'hiver et des Grumegueules peuvent être trouvés dans le village des Tombe-hivers. Même en épique, le voyage vers le village Tombe-hivers prend beaucoup de temps. Il y a des tigres sur la route qui vous étourdiront, ce qui entraînera un désarçonnement, cela devrait être évité (mais peut être difficile car ils vont vous rattraper sur une monture de 60%). \n\n[b]De honoré à exalté[/b]\n\nA partir dhonoré, la troisième quête : [quest=5981] est disponible. La quête exige que le joueur tue 8 géants. Ils sont beaucoup plus difficiles que les Tombe-hivers et le trajet est assez long. Cette quête est généralement ignorée.\n\nEn raison de certains joueurs qui augmentent la réputation des Grumegueules, dans le village de Tombe-hivers, cette quête peut effectivement se révéler une récompense de réputation plus rapide que [quest=5201].'),(8,576,2,NULL,0,2,'[b]Les Grumegueules[/b], dernière tribu furbolg non-corrompue (au moins dans leur point de vue), cherchent à conserver leurs voies spirituelles et à mettre fin à la souffrance de leurs frères.\n\nLes Grumegueules habitent deux zones : [zone=16] et [zone=361]. Ils sont présumés être la seule tribu furbolg à échapper à la corruption démoniaque, mais ce n\'est peut-être pas vrai, en raison de l\'existence de [npc=3897], furbolg de tribu inconnue, et la tribu Stillpine sur [zone=3524]. Cependant, de nombreuses autres races tuent les furbolgs aveuglément maintenant, sans savoir si elles sont alliées ou non. Pour cette raison, les Grumegueles ne se montrent pratiquement pas.\n\nLes aventuriers qui recherchent les Grumegueules dans le nord de Gangrebois et s\'aventurent chez eux apprendront quil faut mieux être leurs alliés. Bien qu\'ils ne possèdent pas de bijoux fins ou de richesses mondaines, la tradition chamanique des Grumegueules est encore forte. Ils connaissent bien l\'art de fabriquer des armures à partir de peaux d\'animaux, et ils sont plus qu\'heureux de partager leurs connaissances de guérison avec des amis de leur tribu. En outre, à partir dune réputation inamical, les Grumegueules vous accorderont également un accès sans problème à [zone=493] et [zone=618] dans leurs tunnels.\n\n[h3] Réputation[/h3]\n\nLa réputation avec la faction des Grumegueules est principalement acquise grâce à des quêtes. Les membres de la tribu Mort-bois, une autre tribu de Furbolg à Gangrebois, sont les principaux ennemis des Grumegueules et peuvent être tué pour gagner de la réputation.\n\n[ul]\n[li] Tuer des furbolgs [url=?Npcs&filter=na=Tombe-hivers]Tombe-hivers[/url] ou [url=?Npcs&filter=na=Mort-bois]Mort-bois[/url], donne 10 points de réputation. Les gains s\'arrêtent à révéré. [/li]\n[li] Tuer [npc=9464] ou [npc=9462], donne 60 points de réputation.[/li]\n[li] Tuer [npc=10738], située dans une grotte à l\'est de [faction=577], donne 50 points de réputation. Son taux de réapparition est de 6 à 8 minutes. [/li]\n[li] Tuer [npc=14342], élite rare, donne 50 points de réputation. Il se situe au village des Mort-bois à Gangrebois. Donne de la réputation jusquà exalté. [/ Li]\n[li] Tuer [npc=10199], élite rare, donne 50 points de réputation. Il se situe dans le village des Tombe-hivers au Berceau-de-lHivers. Donne de la réputation jusquà exalté. [/li]\n[li] Après avoir terminé la quête : [quest=8460], avec les [item=21377] ramassés sur les Furbolgs Mort-bois, la réputation augmente de 150 points. [/li]\n[li] Après avoir terminé la quête : [quest=8464], avec les [item=21383] ramassés sur les furbolgs Tombe-hivers, la réputation augmente de 150 points.[/li]\n[/ul]'),(8,890,2,NULL,0,2,'[b]Les Sentinelles d\'Aile-argent[/b] représente la faction de l\'Alliance sur le champ de bataille [zone=3277]. Les elfes de la nuit, qui ont commencé une avancée massive pour reprendre les forêts de [zone=331], concentrent leur attention sur le débarquement sur leur terre de la [faction=889] une fois pour toutes. Et ainsi, les Sentinelles d\'Aile-argent ont répondu à l\'appel et ont juré qu\'ils ne vont pas se reposer avant que tous les orcs soient vaincus et expulsés du Goulet des Chanteguerres.\n\n[h3]Réputation[/h3]\n\nLes joueurs peuvent gagner leurs réputations, dans cette faction, en participant au champ de bataille du Goulet des Chanteguerres. Vous gagnez 35 points de réputation à chaque fois que votre faction capture un drapeau. Ce gain de réputation est augmenté à 45 les week-ends du champ de bataille.\n\nOn vous accorde le titre : [title=47] une fois quil est exalté avec Les Sentinelles d\'Aile-argent et les deux autres factions des champs de bataille, [faction=730] et [faction=509].'),(8,889,2,NULL,0,2,'[b]Les Voltigeurs Chanteguerre[/b] est un clan orc précédemment dirigé par [npc=18076], daprès lequel le clan a été nommé. Les Voltigeurs Chanteguerre représentent la faction de la Horde sur le champ de bataille [zone=3277], où ils tentent de défendre leurs opérations d\'enregistrement dans [zone=331] de la [faction=890].\n\nCest l\'un des clans les plus forts et les plus violents, le clan de Chanteguerre était également l\'un des clans les plus distingués de Draenor, ce clan a pu échapper aux forces de l\'expédition de l\'Alliance à chaque tournant. Formés comme Grunts, ils ont maîtrisé l\'utilisation d\'épées et de lames et quelques-uns ont même atteint le rang de Maître-lames.\n\n[h3]Réputation[/h3]\n\nLes joueurs peuvent gagner leurs réputations, dans cette faction, en participant au champ de bataille du Goulet des Chanteguerres. Vous gagnez 35 points de réputation à chaque fois que votre faction capture un drapeau. Ce gain de réputation est augmenté à 45 les week-ends du champ de bataille.\n\nOn vous accorde le titre : [title=47] une fois quil est exalté avec Les Voltigeurs Chanteguerre et les deux autres factions des champs de bataille, [faction=510] et [faction=729].'),(8,729,2,NULL,0,2,'[b]Le Clan Loup-de-givre[/b], ainsi que [npc=11946], ont vécu dans [zone=36] et ont des Loups de givre comme compagnons. Des nains, connue sous le nom de [faction=730], ont commencé une expédition dans le territoire des Loup-de-givre pour creuser la vallée et miner les veines. Une transgression envers les Orcs qui habitaient en Alterac. Cela a provoqué lextermination de la première expédition et la bataille pour [zone=2597] a commencé.\n\n[h3]Réputation[/h3]\n\nLes joueurs peuvent gagner leurs réputation, dans cette faction, en participant au champ de bataille de la vallée dAlterac, en effectuant diverses tâches et en tuant les membres de la faction opposée, les Gardes Foudrepiques.\n\nOn vous accorde le titre : [title=47] au joueur une fois quil est exalté avec le clan Loup-de-givre et les deux autres factions des champs de bataille, [faction=889] et [faction=510].'),(8,935,2,NULL,0,2,'[b]Les Sha\'tar[/b], ou \"né de la lumière\", sont des naaru qui ont aidé [faction=932], l\'ordre des prêtres draenei précédemment dirigés par [npc=17468], en reconstruction à [zone=3703]. La ville a été détruite par les Orcs pendant leur fuite à travers Draenor avant la Première Guerre mondiale. \nLa défaite de la Légion ardente est le but ultime des Sha\'tar. Les Sha\'tar sont aidés dans cette guerre par l\'Aldor et leurs rivaux, la faction des elfes du sang connue sous le nom : [faction=934]. \nL\'Aldor et les Clairvoyants se battent pour la faveur du Sha\'tar afin qu\'ils puissent être aidés dans leur guerre pour les pouvoirs des naaru. L\'entité qui dirige le Sha\'tar est connue sous le nom de [npc=18481] ; Il peut être trouvé sur la terrasse de la lumière dans la ville de Shattrath.\n\nLes joueurs de l\'Alliance et de la Horde commencent avec une réputation neutre auprès des Sha\'tar. Les joueurs peuvent augmenter leur réputation, Sha\'tar, à travers diverses quêtes, en élevant leur réputation avec lAldor ou les clairvoyants, ou en s\'aventurant dans le [url=?search=donjon+tempête]donjon des tempêtes [/url].\n\n[h3]Réputation[/h3]\n\n[b]Jusqu\'à honoré[/b]\n\nLa réputation peut être obtenue à partir de divers objets. Ce qui suit n\'accordera que de la réputation de Sha\'tar jusqu\'à ce que vous obteniez un statut honoré : \n[li]Pour une réputation envers les Clairvoyants : [item=29426], [item=30810] et [item=29739][/li]\n[li]Pour une réputation envers l\'Aldor : [item=29425], [item=30809] et [item=29740][/li]\n\n[i]Notez que ce gain de réputation ne s\'affiche pas dans le journal de combat, mais peut être vérifié en regardant votre panneau de réputation.[/i]\n\nLa réputation peut également être obtenue en faisant le temple des tempêtes : [zone=3847], [zone=3846] et [zone=3849].\n\n[b]Jusquà exalté [/b]\n\nAprès avoir épuisé les récompenses de réputation de Aldor ou des Clairvoyants, les joueurs souhaiteront peut-être compléter les quelques quêtes de Sha\'tar disponibles. En plus des quêtes, les instances qui se trouvent au temple des tempêtes : Botanica, Arcatraz et Mechanar continueront à accorder de la réputation. À ce stade, il est probablement plus utile d\'exécuter ces instances en mode héroïque.'),(8,934,2,NULL,0,2,'[b]Les Clairvoyants[/b] sont des elfes de sang qui résident dans [zone=3703] dirigé par [npc=18530]. Le groupe s\'est éloigné de [npc=19622] et a offert de leur aide au Naaru de Shattrath. Ils sont en désaccord avec [faction=932], et rivalisent avec eux pour le pouvoir de Shattrath et la faveur du Naaru. \n\nLa plupart des joueurs commenceront avec une réputation neutre auprès des Clairvoyants. [npc=18166] à Shattrath donnera aux joueurs une première quête pour devenir amical avec lAldor ou Les Clairvoyants. Ce choix est réversible si les joueurs ressentent le besoin. \nLes joueurs delfes de sang seront amicaux avec les Clairvoyants et hostiles avec l\'Aldor, alors que les joueurs draenei seront hostiles aux Clairvoyants et amicaux envers lAldor.\n\n[npc=19331] et [npc=20808] sont situés dans la banque des Clairvoyants, sur le bord sud de la terrasse de lumière. La Bibliothèque du Visiteur abrite [npc=20613] [small][/small] et [npc=21905] [small][/small], qui échangent des pièces d\'armure épique contre des pièces de set de[url=?Itemsets&filter=ta=12]Niveau 4[/url] et de [url=?Itemsets&filter=ta=13]Niveau 5[/url].\n\n[i]Note : Les gains de réputation avec les Clairvoyants correspondent à une perte de réputation de 10% plus élevée chez lAldor. La plupart des gains de réputation avec les Clairvoyants accorderont également 50% de la réputation avec [faction=935].[/i]\n\n[h3]Tradition [/h3]\n\nAprès avoir subi des assauts implacables de leurs ennemis, les gardes harassés de Sha\'tar et de lAldor se sont regroupés pour la prochaine attaque alors qu\'elle marchait sur l\'horizon. Cette fois, l\'attaque provenait des armées de [npc=22917]. Un grand régiment d\'elfes de sang avait été envoyé par l\'allié d\'Illidan, le prince Kael\'thas pour détruit la ville. Alors que le régiment d\'elfes de sang traversait le pont, les exarques et les vindicateurs de lAldor se sont alignés pour défendre la Terrasse de Lumière. Alors l\'inattendu arriva, les elfes de sang déposèrent leurs armes devant les défenseurs de la ville.\nLeur chef, un ainé de sang connu sous le nom de Voren\'thal, a exigé de parler au naaru [npc=18481]. À mesure que le naaru s\'approchait de lui, Voren\'thal s\'agenouilla et prononça les mots suivants : « Je vous ai vu dans une vision, naaru. Le seul espoir de survie de ma race est avec vous. Mes disciples et moi-même sommes là pour vous servir ».\nLa défection de Voren\'thal et de ses partisans a été la plus grande perte jamais subie par les forces de Kael\'thas. Beaucoup des plus forts et les plus brillants parmi les savants et les magistrats de Kael\'thas ont été influencés par l\'influence de Voren\'thal. Le naaru a accepté les déflecteurs qui sont devenus connus sous le nom de Clairvoyant.\n\n[h3]Réputation[/h3]\n\n[b]Jusqu\'à honoré[/b]\n\nLes joueurs qui cherchent à gagner les rangs de réputation supérieurs (Révéré, Exalté) peuvent vouloir sauver des quêtes non répétables jusqu\'à ce qu\'ils soient honorés.\n\nDonner 10 [span class=q1][item=29426][/span] à [npc=18531] dans la bibliothèque du Visiteur des Clairvoyants accordera une réputation de 250 points de réputation pour les Clairvoyants. Il existe également une quête répétable où donner une unique chevalière accorde 25 points de réputation. Ces chevalières tombent sur des membres Aile-de feu dans la partie nord-est de la forêt de Terrokar. \nEnviron 240 marques sont nécessaires pour passer d\'amical à honoré.\nEn outre, ces quêtes fournissent de la réputation de Sha\'tar ; 125 points de réputation pour 10 marques ou 12,5 points de réputation pour une unique chevalière.\n\n[b]Jusqu\'à exalté [/b]\n\nUne fois que vous atteignez le niveau 68, vous pouvez également donner 10 [span class=q1][item=30810][/span], cest le même principe que les chevalières mais ceux-ci tombent sur des elfes de sang Solfurie de haut rang. Si vous le souhaitez, vous pouvez transformer les chevalières de niveau supérieur avant une réputation honorée. Vous les trouverez dans [zone=3523], [zone=3520] et les instances du [url=?Search=tempête+donjon]donjon de la tempêtes[/url].\n\n[b]Tome des Arcanes[/b]\n\n[span class=q2][item=29739][/span] peut être donné à tout moment à [npc=18530] à l\'intérieur la Bibliothèque du Visiteur. Cela augmentera votre réputation avec les Clairvoyants de 350 par Tome des Arcane.\nEn plus des gains de réputation, vous recevrez une [span class=q1][item=29736][/span], qui est la condition pour acheter l\'enchantements d\'épaule à [npc=20808], qui réside dans la banque des Claivoyants.\n\n[h3]Passer à la réputation des Claivoyants[/h3]\n\nPour changer votre faction d\'Aldor vers Claivoyants et donc accéder à leurs recettes d\'artisanat (et annuler toutes les avancées de réputation que vous avez faites), trouvez [npc=18596], membre des Claivroyants dans la ville basse. Elle vous propose une quête répétable, [quest=10024], où pour huit [span class=q1][item=25744][/span] vous montez la réputation Claivoyant. Une fois que vous êtes neutre, vous ne pourrez plus recevoir cette quête.'),(8,942,2,NULL,0,2,'L[b]Expédition Cénarienne[/b] a été envoyé par [faction=609], lors de la réouverture de la porte des ténèbres vers l\'Outreterre, pour explorer ce monde inconnu. Tout comme le cercle, il s\'agit d\'une coalition de forces entre les Elfes de la nuit et les Taurens. Depuis l\'ouverture de la porte, l\'expédition Cénarienne a rapidement gagné en taille et en autonomie, obtenant suffisamment de puissance pour être considérée comme une propre et unique faction. L\'expédition maintient sa base principale au refuge Cénarien dans [zone=3521], située immédiatement à louest de la péninsule des flammes infernales. Elle est aussi présente sur [zone=3483], dans [zone=3519], et dans [zone=3522]. \n\nLe Refuge est situé dans le marécage de Zangar afin détudier la faune riche située là-bas. Cependant, l\'expédition a révélé des retombées inquiétantes dans le marais. Les niveaux d\'eau dans de nombreuses régions du marécage diminuent, et certaines régions comme Morte-bourbe ont déjà beaucoup souffert de ce phénomène étrange. On sait que cette diminution des niveaux d\'eau peut être attribuée aux pompes qui ont été construites dans le marécage par les naga. Leur but est de créer un nouveau puits d\'éternité pour [npc=22917].\nCependant, l\'expédition ne peut pas se permettre une confrontation directe avec le naga si nombreux dans le marécage de Zangar et le [url=?Search=Glissecroc#c0z]Réservoir de Glissecroc [/url]. Elle a besoin de l\'aide daventurier qui veulent soutenir les druides dans leur dangereuse bataille contre les Nagas qui cherchent à perturber l\'équilibre naturel du marais. Naturellement, ceux assez héroïques pour combattre au réservoir de Glissecroc seront bien récompensés.\n\n[h3]Réputation[/h3]\n\n[b]De neutre à honoré[/b]\n\nTuez des Nagas chaque fois que vous le pouvez. Le mieux sera de parcourir les instances, la réputation monte plus rapidement.\nAlternativement, le joueur peut commencer à trouver des [item=24401] pour avoir une chance davoir des [item=24407], qui peuvent être transformé en 500 points de réputation. Il est suggéré que le joueur garde ses espèces non cataloguées jusqu\'à ce que son statut honoré soit atteint, car la quête ne peut pas être poursuivie après ce point, alors que les espèces non cataloguées peuvent être utilisées jusqu\'à Exalté.\n\nSi vous êtes un herboriste et que vous êtes intéressé par la réputation [faction=970], vous voudrez peut-être trouver les [url=?Npcs&filter=na=Seigneur+tourbe]Seigneurs-tourbes[/url] qui se trouve dans lEst, et le coin Sud-ouest du Marécage de Zangar. Leurs corps peuvent être «récoltés» par les herboristes et produisent souvent des végétaux non identifiées, alors que chaque monstre tué donne 15 points de réputation chez Sporeggar. \n\n[b]De honoré à révéré[/b]\n\nUne fois que le joueur est honoré, faire lenclos aux esclaves et [zone=3716] (à l\'exception de [npc=17770] et de certains géants), n\'accorderont plus de réputation. Vous devriez maintenant faire des quêtes de l\'Expédition Cénarienne dans la péninsule des flammes infernal, le marécage de Zangar, la forêt de Terokkar et les Tranchantes. Il est également temps de transformer toutes les espèces non cataloguées que vous avez trouvées. Faire cela devrait vous faire passer révérer.\n\nAlternativement, vous pouvez, en étant niveau 70, faire [zone=3715]. Chaque donjon donne un peu plus de 1500 points de réputation si vous tuez toutes les mobs.\nDans le Caveau de la vapeur, se trouve, aussi, une quête répétable, [quest=9764], qui commence par [item=24367]. Vous pourrez ensuite donner les [item=24368], qui tombe à la fois dans le caveau de la vapeur et lenclos aux esclaves, recevant 250 points de réputation pour les premières armes et 75 points de réputation par la suite. Cette quête est disponible jusqu\'à exalté.\n\nUne fois que vous avez le niveau 70 et que vous avez amélioré votre équipement, vous pouvez choisir d\'entrer dans lenclos des esclaves, le caveau de la vapeur et basse-tourbière en mode héroïque avec l\'achat de la [item=30623]. Ils accordent une réputation importante : les mobs ordinaires valent 15 points de réputation, 2 pour les non élites et 150 à 250 pour les boss. Cette méthode fonctionne jusqu\'à exalté.\n\n[b]De révéré à exalté [/b]\n\nContinuez avec la même stratégie que ci-dessus : terminez toutes les requêtes restantes, faites caveau de la vapeur et continuez avec la quête des [item=24368].\n\nIl est également possible de faire lenclos des esclaves, Basse-tourbière et caveau de la vapeur en mode héroïque. La réputation acquise n\'est pas beaucoup plus intéressante que le caveau de la vapeur en mode normal, alors que l\'investissement dans le temps pour les donjons héroïques est beaucoup plus élevé, le butin est mieux et vous recevrez [item=29434] sur les boss qui peuvent être utilisés pour acheter des équipements épiques de haute qualité.'),(8,941,2,NULL,0,2,'Les [b]Mag\'har[/b] sont la faction d\'orcs à peau brune qui sont restées en Outreterre et se sont séparés des autres clans orcs restants qui ont été victimes de [npc=17257] et qui sont maintenant dirigés par le puissant [npc=16808]. Les Mag\'har sont présent dans la forteresse de Garadar dans le magnifique pays de [zone=3518], une fois bien installés, la majorité des orcs sont retournés dans [zone=3519] et [zone=3522].\n\nLes Maghar n\'ont jamais été corrompus par Mannoroth ou Magtheridon. Contrairement à dautres anciens clans qui vivent dans les ruines de leurs ancêtres, les Mag\'har sont composés de membres de différents clans d\'orc qui ont échappé à la corruption. Le chef actuel des Mag\'har, la vénérable [npc=18141], est une orc ancienne et sage, mais elle est tombée récemment extrêmement malade. [npc=18063], fils du puissant Grom hurlenfer, sert de chef militaire aux Mag\'har, aidé par [npc=18106], fils du vénérable chef du clan Orbite-Sanglante, Kilrogg Deadeye. En outre, il existe un orc dans un camp de Mag\'har à l\'ouest connu sous le nom [npc=18229].\n\nIl n\'est pas clair comment le Mag\'har a réussi à conserver sa peau marron d\'origine. La peau orque devient verte lorsqu\'elle est exposée à la magie du sorcier, indépendamment des croyances ou des pratiques de l\'individu ; Garrosh et Jorin auraient certainement été exposés, compte tenu de la position hiérarchique de leurs pères.\n\nLes joueurs de la Horde commencent inamical avec le Mag\'har. Les joueurs de l\'Alliance seront toujours traités comme hostiles. La contrepartie de l\'Alliance à cette faction est la faction des : [faction=978].\n\n[h3]Quête[/h3]\n\nLes quêtes pour les Mag\'har commencent dans [zone=3483] avec [quest=9400] de [faction=947]. Cette quête vous mènera à un petit avant-poste Mag\'har au nord de la Citadelle des flammes infernales. Une fois à Nagrand, les joueurs trouveront la principale ville de Mag\'har, Garadar. La ville détient la plupart des quêtes restantes qui récompenseront la réputation de Mag\'har.\n\n[i]Note : Vous DEVEZ compléter la suite de quête de \"lassassin\" jusqu\'à la quête [quest=9410] (où vous devenez neutre) afin que vous puissiez parler à la plupart des gens de Garadar.[/i]\n\n[h3]Réputation[/h3]\n\nLa réputation peut être obtenue en tuant des [url=?npcs&filter=na=kil%27sorrau;ra=-1;rh=-1]Membres de culte Kil\'sorrau[/url], des [url=?Npcs&filter=na=Bourbesang;ra=-1;rh=-1;cr=6;crs=3518;crv=0]Bourbesang[/url], des [url=?Npcs&filter=na=cogneguerre+-marker]Cogneguerre[/url] et des [url=?Npcs&filter=Na=rochepoing;minle= 64;ra=-1;rh=1]Rochepoing[/url] à Nagrand. Les joueurs peuvent également transformer 10x[item=25433], qui tombent de ces ogres.\n\nLes joueurs qui recherchent la réputation : [faction=933] peuvent vouloir garder leurs perles, car la réputation Mag\'har est généralement plus facile à obtenir. \nLes joueurs qui recherchent la réputation :[faction=932] peuvent préférer tuer les membres du culte à la forteresse de Kil\'Sorrau, car ils donnent aussi des [item=29425] pour la réputation Aldor.\n\n[i]Remarque : Ces monstres et quêtes n\'ont pas de limite, ils accordent une réputation jusquà exalté![/i]'),(8,946,2,NULL,0,2,'Le [b]Bastion de lHonneur[/b], refuge des explorateurs humains, élu, draenei et nains, est la première grande ville que les explorateurs de l\'Alliance rencontreront en traversant la porte des ténèbres. Les vestiges des fils de Lothar, anciens combattants de l\'Alliance qui sont venus à Draenor, se sont tenus fermement dans cet avant-poste des flammes infernales. Ils sont maintenant rejoints par les armées de Hurlevent et Forgefer.\n\n[h3]Réputation[/h3]\n\nLa réputation du Bastion de l\'Honneur est gagnée par divers moyens dans la péninsule des flammes infernales. Les PNJs, dans et autour, de la citadelle donnent en récompensés de quêtes de l\'honneur et de la réputation. En raison du manque de représentants dans d\'autres endroits dOutreterre il y a un grand écart entre Honoré et Exalté, au cours duquel il est possible que vous ne puissiez pas obtenir assez de réputation au bastion de lhonneur une fois que vous partez de la péninsule.\n\n[b]Jusquà Honoré[/b]\n\nTuer des Pnjs dans [zone=3562] et [zone=3713] attribueront de la réputation. Une option est de faire les donjons jusqu\'à ce que la réputation arrive à honoré avant de faire des quêtes du Bastion de l\'honneur, car les quêtes continuent à donner de la réputation jusqu\'à Exalté.\n\nVous voudrez peut-être tuer les orcs à lextérieur du bastion qui donnent une réputation si vous êtes Neutre. La réputation donnée sarrête une fois que vous êtes amicales.\n[ul]\n[li][npc=19415][/li]\n[li][npc=16878][/li]\n[li][npc=16870][/li]\n[li][npc=16867][/li]\n[li][npc=19414][/li]\n[li][npc=19413][/li]\n[li][npc=19411][/li]\n[li][npc=19422][/li]\n[/ul]\n\n[b]PvP[/b]\n\nLes joueurs qui apprécient le PvP peuvent gagner de l\'honneur et de la réputation avec la quête [quest=10106]. Cette quête accorde 70 points d\'honneur et 150 points de réputation au Bastion de lHonneur, mais ne peut être complétée qu\'une fois par jour et compte pour votre limite de 25 quêtes journalières. L\'achèvement de cette quête fournit également trois [span class=q1][item=24579][/span], qui sont utilisés comme monnaie pour divers types d\'articles lorsqu\'ils sont échangés chez [npc=17657] et [npc=18266] au Bastion de lHonneur ainsi que [npc=18581] aux marécages de Zangar.\n\n[b]Jusquà Exalté[/b]\n\nÀ partir de là, il n\'y a que deux façons d\'atteindre Révéré et Exalté :\n[ul]\n[li][zone=3714], cette instance nécessite le niveau 68 et [span class=q1][item=28395][/span] (Un seul membre du groupe a besoin de la clé). Linstance des salles brisées abrite des PNJs qui donnent de la réputation jusquà Exalté.[/li]\n[li]Après avoir obtenu le statut dhonoré, vous pouvez acheter [span class=q1][item=30622][/span] qui accorde l\'accès au mode héroïque des instances de la citadelle des flammes infernales. Faire les donjons en mode Héroique donneront plus de réputation que les salles brisées en mode normale et continueront à donner de la réputation jusquà Exalté.[/li]\n[/ul]\n\n[i]Astuce : Vous pouvez utiliser ces marques pour acheter [span class=q1][item=24520][/span] à l\'adjudant Tracy Proudwell et augmenter le montant gagné de réputation (et dexpérience) acquise lors de l\'exécution de ces instances.[/i]'),(8,967,2,NULL,0,2,'[b]L\'Oeil Pourpre[/b] est une secte secrète fondée par le Kirin Tor de Dalaran pour espionner le gardien de Tirisfal, [npc=15608], dans la tour de [zone=2562]. Bien que Medivh soit mort, l\'il pourpre reste dans Karazhan, défendant le mal qui semble lenvahir en l\'absence de son maître.\n\nOn ignore si l\'apprenti de Medivh, [npc=18166], était membre de lOeil Pourpre, ou s\'il connaissait leurs activités à l\'époque.\n\n[h3]Réputation[/h3]\n\nLa réputation de lil pourpre est obtenue en tuant des mobs à l\'intérieur de Karazhan et en complétant les quêtes liées à Karazhan. La réputation grâce aux mobs de Karazhan peut être acquise à partir d\'une position neutre jusquà une réputation exalté. Chaque mob apporte une réputation d\'environ 15 points, les boss accordent davantage de réputation.\n\n[npc=18253] propose une chaîne de quête assez longue commençant par [quest=9824] et [quest=9825]. Cette suite de quête se termine par [quest=9644] et récompense les joueurs avec [span class=q1][item=24490][/span]. L\'achèvement complet de cette suite de quête récompense le joueur avec 10 270 point de réputation d\'environ.\n\n[h3]Récompenses de la réputation[/h3]\n\n[npc=18253] offrira aux joueurs des bagues en récompenses pour chaque niveau de réputation sous forme de quêtes. La première de ces quêtes est disponible dès la réputation neutre. Vous recevrez une version nouvelle et améliorée de la bague que vous avez choisi chaque fois que vous entrez dans un nouveau niveau de réputation. Les anneaux sont triés dans les 4 catégories suivantes :\n[ul]\n[li][quest=10731] : [item=29280], [item=29281], [item=29282] et [item=29283][/li]\n[li][quest=10729] : [item=29284], [item=29285], [item=29286] et [item=29287][/li]\n[li][quest=10732] : [item=29276], [item=29277], [item=29278] et [item=29279][/li]\n[li][quest=10730] : [item=29288], [item=29289], [item=29291] et [item=29290][/li]\n[/ul]\n\n[npc=16388], un forgeron situé à l\'intérieur de Karazhan juste après [npc=15550], offre aux joueurs ayant une réputation assez élevée la possibilité d\'acheter des plans de forge épique. Les joueurs honorés ou au-dessus pourront également réparer des armures et des armes chez ce fournisseur.\n\n[npc=18255], qui se trouve juste à l\'extérieur des portes principales de Karazhan, vendra une recette de joaillerie épique et un enchantement d\'épaule aux joueurs qui ont une haute réputation avec lOeil Pourpre.'),(8,970,2,NULL,0,2,'Les[b]Sporeggar[/b] sont une race de champignons essentiellement pacifique originaire d\'Outreterre. Ils vivent dans une ville située dans les tourbières occidentales de [zone=3521].\n\n[h3]Réputation [/h3]\n\nLes joueurs de l\'Alliance et de la Horde commencent amicalement avec Sporeggar. Il existe de nombreuses façons d\'augmenter votre réputation au début : \n[ul]\n[li]Apporter 10 [span class=q1][item=24290][/ span] à [npc=17923] pour compléter [quest=9739][/li]\n[li]Apporter 6 [span class=q1][item=24291][/span] à Fahssn pour compléter [quest=9743][/li]\n[i]Ces deux quêtes ne seront disponibles que si vous avez une réputation au minimum amical[/i]\n[li]Tuer [url=?Search=seigneurs +tourbes+-hungry #z0z]Seigneurs tourbes[/url] [i](jusqu\'à honoré)[/i][/li]\n[li]Tuer [npc=18137] et [npc=18136] [i](jusqu\'à révéré)[/i][/li]\n[li]Apporter 10 [span class=q1][item=24245][/span] à [npc=17924] dans Sporeggar[i] (jusquà amical)[/i][/li]\n[/ul]\n\nAprès avoir une réputation [b]amicale[/b], de nouvelles quêtes répétitives s\'ouvrent en même temps que les quêtes de Fahssn, notamment :\n[ul]\n[li]Tuer 12 [npc=18088] et [npc=18089] pour [npc=17856] pour compléter [quest=9726][/li]\n[li]Apporter 10 [span class=q1][item=24449][/span] à [npc=17925] pour compléter [quest=9806][/li]\n[li] S\'aventurer dans [zone=3716] pour rassembler 5 [span class=q1][item=24246][/span] pour terminer [quest=9715][/li]\n[/ul]\nCes 3 quêtes sont répétables et seront disponibles jusquà la réputation exalté.\nLes joueurs qui sont exaltés avec Sporeggar devraient parler à [npc=17877] pour une dernière quête.'),(8,978,2,NULL,0,2,'Les Kurenaï, pour « racheté », ont échappé à lesclavage en Outreterre et ont fait leur maison à Telaar dans le sud de [zone=3518]. C\'est là qu\'ils cherchent à redécouvrir leur destinée. Ils conservent également une petite présence en [zone=3521]. Leur intendant, [npc=20240], est situé à l\'extérieur de l\'auberge à Telaar, en dessous du point de vol.\n\nLes joueurs de l\'Alliance commencent à faire preuve d\'hostilité avec les Kurenai. Les joueurs de la Horde seront toujours traités comme hostiles. La contrepartie de la Horde à cette faction est [faction=941].\n\n[i]Kurenai est le japonais pour « cramoisi ».[/i]\n\n[h3]Réputation[/h3]\n\nLa réputation peut être obtenue en tuant des [url=?Npcs&filter=na=kil%27sorrau;ra=-1;rh=-1]Membres de culte Kil\'sorrau[/url], des [url=?Npcs&filter=na=Bourbesang;ra=-1;rh=-1;cr=6;crs=3518;crv=0]Bourbesang[/url], des [url=?Npcs&filter=na=cogneguerre+-marker]Cogneguerre[/url] et des [url=?Npcs&filter=Na=rochepoing;minle= 64;ra=-1;rh=1]Rochepoing[/url] à Nagrand. Les joueurs peuvent également transformer 10x [item=25433], qui tombent de ces ogres.\n\nLes joueurs qui cherchent la réputation de la faction [faction=933] peuvent vouloir garder leurs perles, car la réputation de Kurenai est généralement plus facile à obtenir.\n\nLes joueurs qui cherchent la réputation de la faction [faction=932] peuvent préférer tuer les membres du culte à la forteresse de Kil\'Sorrau, alors qu\'ils donnent des [item=29425] pour la réputation de lAldor.\n\n[i]Remarque : Ces monstres et quêtes n\'ont pas de limite, ils accordent de la réputation jusquà exalté.[/i]'),(8,989,2,NULL,0,2,'Les [b]Gardiens du Temps[/b] sont des dragons de bronze sélectionnés par Nozdormu pour surveiller les grottes du temps. Ils sont dirigés par [npc=19932] et [npc=19933], qui remplacent également Nozdormu en son absence.\n\n[h3]Réputation[/h3]\n\nActuellement, la seule façon d\'obtenir la faveur des dragons de bronze est de faire les instances : [zone=2367] et [zone=2366]. Lintendant des Gardiens du Temps, [npc=21643], se situe au quartier-intendant dans les grottes du temps. Les Gardiens vous demanderont d\'être au minimum niveau 66 et de compléter la courte quête [quest=10277] avant d\'autoriser le passage dans Les contreforts dHautebande dantan pour accomplir la destinée du Chef de la Horde, [npc=17876].'),(8,990,2,NULL,0,2,'La [b]Balance des sables[/b] est un sous-groupe secret du vol des Dragons de bronze, dirigé par [npc=19935], premier partenaire de [npc=15185]. Leur chef, Nozdormu, a envoyé ces factions gardiennes à [zone=3606] où ils gardent l\'Arbre Monde d\'une autre attaque par les démons, contribuent à restaurer le temps et à préserver l\'avenir du monde.\n\n[h3]Réputation[/h3]\n\nTuer les boss et monstres du Fléau font monter la réputation. [npc=17968], le boss final, récompense de 1 500 points de réputation tandis que les quatre autres boss donnent 375 points de réputations. La réputation général des montres du Fléau donnent 12 points de réputation, tandis que [npc=17907] donnent 60 points de réputation. En produisant une moyenne de 7 800 points de réputations par raid, 6 raids sont nécessaires pour atteindre la réputation exaltée.\n\nActuellement, la réputation permet davoir lune des meilleurs [span class=q4][url=?Items=4.-2&filter=na=bague+éternel]Bagues[/url][/span] pour les raids. Afin de recevoir ces anneaux, vous devez compléter la quête précédemment requise, [quest=10445]. Chaque nouveau niveau de réputation accorde une bague améliorée.'),(8,1012,2,NULL,0,2,'Les [b]Ligemorts Cendrelangues[/b] sont l\'élite de la tribu Kurenaï connue sous le nom de Cendrelangue. La tribu Cendrelangue est dirigée par la sage aînée [npc=21700]. Les Ligemorts sont [i]officiellement[/i] alignés avec [npc=22917] [small][/small]. Les Ligemorts sont les lieutenants les plus dignes d\'Akama et sont au courant des motivations mystérieuses de leur chef.\n\nPour découvrir les Ligemorts Centrelangues en tant que faction, le joueur doit commencer et compléter la majorité de la suite de quête qui commence par [quest=10568] ou [quest=10683]. Finalement, vous parlerez avec Akama, après quoi vous deviendrez neutre avec les Ligemorts Cendrelangues.'),(8,947,2,NULL,0,2,'[b]Thrallmar[/b], expédition envoyée par le Portail des Ténèbres par Thrall, a construit un bastion dans la péninsule des flammes infernales qui sert de base d\'opérations pour une grande partie des activités de la Horde en Outreterre.\n\n[h3]Réputation[/h3]\n\nLa réputation de Thrallmar jusqu\'à l\'honorée est relativement facile à gagner. Même les quêtes les plus faciles (celles qui vous emmènent d\'un fournisseur de quête à la prochaine, par exemple) peuvent produire 75 points de réputation, alors que ceux qui nécessitent plus defforts pour compléter ont généralement 250 points de réputation ou plus. Certaines quêtes de groupe impliquant de tuer un élite peuvent donner jusqu\'à 1 000 points de réputation.\n\nSi vous faites la majeure partie des quêtes de Thrallmar au lieu de passer rapidement à la prochaine zone, vous pourriez vous attendre à être honoré après 1 ou 2 niveaux de jeu. En raison du manque de représentants dans d\'autres endroits dOutreterre il y a un grand écart entre Honoré et Exalté, au cours duquel il est possible que vous ne puissiez pas obtenir assez de réputation à Thrallmar une fois que vous partez de la péninsule. Cest seulement au niveau 68 que vous pouvez commencer à regagner des points dans le donjon [zone=3714].\n\n[b]Jusquà Honoré[/b]\n\nTuer des Pnjs dans [zone=3562] et [zone=3713] attribueront de la réputation. Une option est de faire les donjons jusqu\'à ce que la réputation arrive à honoré avant de faire des quêtes de Thrallmar, car les quêtes continuent à donner de la réputation jusqu\'à Exalté.\n\nVous voudrez peut-être tuer les orcs à lextérieur du bastion qui donnent une réputation si vous êtes Neutre. La réputation donnée sarrête une fois que vous êtes amicales.\n[ul]\n[li][npc=19415][/li]\n[li][npc=16878][/li]\n[li][npc=16870][/li]\n[li][npc=16867][/li]\n[li][npc=19414][/li]\n[li][npc=19413][/li]\n[li][npc=19411][/li]\n[li][npc=19422][/li]\n[/ul]\n\n[b]PvP[/b]\n\nLes joueurs qui apprécient le PvP peuvent gagner de l\'honneur et de la réputation avec la quête [quest=10110]. Cette quête accorde 70 points d\'honneur et 150 points de réputation à Thrallmar, mais ne peut être complétée qu\'une fois par jour et compte pour votre limite de 25 quêtes journalières. L\'achèvement de cette quête fournit également trois [span class=q1][item=24581][/span], qui sont utilisés comme monnaie pour divers types d\'articles lorsqu\'ils sont échangés chez [npc=18267] et [npc=18564] à Thrallmar et près de Zabrajin dans [zone=3521].\n\n[b]Jusquà Exalté[/b]\n\nÀ partir de là, il n\'y a que deux façons d\'atteindre Révéré et Exalté :\n[ul]\n[li][zone=3714], cette instance nécessite le niveau 68 et [span class=q1][item=28395][/span] (Un seul membre du groupe a besoin de la clé). Linstance des salles brisées abrite des PNJs qui donnent de la réputation jusquà Exalté.[/li]\n[li]Après avoir obtenu le statut dhonoré, vous pouvez acheter [span class=q1][item=30637][/span] qui accorde l\'accès au mode héroïque des instances de la citadelle des flammes infernales. Faire les donjons en mode Héroique donneront plus de réputation que les salles brisées en mode normale et continueront à donner de la réputation jusquà Exalté.[/li]\n[/ul]\n\n[i]Astuce : Vous pouvez utiliser ces marques pour acheter [span class=q1][item=24522][/span] au Crieur-de-guerre Coquard et augmenter le montant gagné de réputation (et dexpérience) acquise lors de l\'exécution de ces instances.[/i]'),(8,1011,2,NULL,0,2,'[b]Ville Basse[/b] de [zone=3703] est l\'endroit où les réfugiés se rassemblent et saident par leurs propres moyens. Lorsque vous aidez l\'une des races qui ont fui la guerre, la réputation se débrouille rapidement. Leur intendant, [npc=21655], est situé sur le marché dans la ville basse.\n\nLa ville basse de Shattrath contient de nombreux artisans qui possèdent de vastes connaissances :\n[ul]\n[li][npc=19187], [small]< Maître des travailleurs du cuirs >[/ small].[/li]\n[li][npc=19180], [small]< Maître des dépeceurs >[/small].[/li]\n[li][npc=19052], [small]< Maître des alchimistes >[/small]. Il donne la quête [quest=10902] (pour une spécialisation). Un laboratoire dalchimiste se trouve également à son côté.[/li]\n[li]Trois tailleurs qui vous permettent de se spécialiser et d\'acheter de nouvelles recettes de couture épiques pour des ensembles d\'armures et des sacs spéciaux :\n[ul][li][npc=22212], [small]< Spécialiste de couture de tisse-ombre >[/small] vend des recettes pour [itemset=553][/li]\n[li][npc=22213], [small]< Spécialiste de couture de feu-sorcier >[/small] vend des recettes pour [itemset=552].[/li]\n[li][npc=22208], [small]< Spécialiste de couture détoffe lunaire > [/small] vend des recettes pour [itemset=554].[/li][/ul]\n[/ul]\n\nLes maîtres de guerre, Alliance et Horde, des quatre [zones=6] peuvent également être trouvés ici, ainsi que la Tavernes de la Fin du Monde.\n\n[h3]Réputation[/h3]\n\n[b]Jusqu\'à honoré [/b]\n[ul]\n[li]Faire [zone=3790] en [i]mode normal[/i], vous récompense denvirons 750 points de réputation.[/li]\n[li]Faire [zone=3791] en [i]mode normal[/i], vous récompense denvirons 1 250 points de réputation.[/li]\n[li]Faire [zone=3789] en [i]mode normal[/i], vous récompense denvirons 2 000 points de réputation.[/li]\n[li]Fournir 30 x [item=25719] à [npc=22429], vous récompense de 250 points de réputations par quête.[/li]\n[/ul]\n[i]Note : Les joueurs qui visent une faction supérieure à Honorée devraient attendre jusqu\'à dêtre honoré avant de compléter les quêtes de la Ville Basse.[/i]\n\n[b]De honoré à exalté[/b]\n[ul]\n[li]Faire de Labyrinthe des ombres en [i]mode normal[/i], vous récompense de 2 000 points de réputation.[/li]\n[li]Terminer toutes les [url=?quests&filter=cr=1;crs=1011;crv=0]quête de la Ville-Basse[/url].[/li]\n[/ul]\n[b]De révéré à exalté[/b]\n[ul]\n[li]Faire les Cryptages Auchenai en [i]mode héroïque[/i], vous récompense denvirons 750 points de réputation.[/li]\n[li]Faire les salles de Sethekk en [i]mode héroïque[/i], vous récompense denvirons 1 250 points de réputation.[/li]\n[li]Faire le Labyrinthe des ombres en [i]mode normal[/i] ou en [i]mode héroïque[/i], vous récompense denvirons 2 000 points de réputation.[/li]\n[/ul]\n\n[h3]Anecdotes[/h3]\n\n[npc=19227], un vendeur dans la ville basse, vend des amulettes qui sont très ... intéressantes. Il vend des articles comme [item=27940], qui vous permettent de revenir à la vie lorsque vous retournez à l\'endroit où vous êtes mort. [i]Buyer se méfiez-vous![/i]\n\nEn tant quexalté, vous pouvez acheter un [item=31778]. Curieusement, aucun des habitants de la Ville Basse na été vu avec un tel objet. Peut-être qu\'ils ne peuvent pas se le permettre'),(8,1015,2,NULL,0,2,'L[b]Aile-du-Néant[/b] est une faction de dragons situés en Outreterre. La couvée inhabituelle a été engendrée par les ufs du vol de dragon noir dAile-de-Mort et infusée d\'énergies brutes. Maintenant, ils cherchent à trouver leur identité au-delà de l\'ombre du patrimoine destructeur de leur père.\n\n[h3]Réputation[/h3]\n\nLes joueurs, au commencement, sont haïe à la faction Aile-du-Néant et doivent être exaltés pour recevoir des [span class=q4][url=?Items=15.-7&filter=na=Aile-du-Néant+Drake]Drakes Aile-du-Néant[/url][/spanclass]. La suite de quête de la réputation est une suite qui se fait en solitaire impliquant des quêtes journalières, une quête de groupes (5 joueurs) pour passer Neutre et les quêtes journalières de groupe (3 joueurs) après être passer Révéré.\nUne monture volante est requise pour cette réputation et 300 compétences de monte sont nécessaires pour passer neutre.\n\n[b]De Haïe à Neutre[/b]\n\nLes joueurs de niveau 70 commenceront leur voyage pour une réputation exaltée en choisissant la suite de quête offerte par [npc=22113], un elfe du sang errant la surface des champs dAile-du-Néant, dans le coin sud-est de [zone=3520]. La suite de quête commence par [quest=10804]. L\'achèvement de cette suite fournira une réputation instantanée neutre et le choix de l\'un de [span class=q3][url=?Items&filter=qu=18;cr=1;crv=0;na=Aile%20néant;qu=3]ces 5 items[/url][/span].\n\n[h3]Après Neutre [/h3]\n\nAprès avoir terminé la suite de quête, Mordenai sassurera qui vous ayez acquis 300 compétences [spell=34091] et que vous ayez une réputation neutre auprsè de lAile-de-Néant.\nCela vous accordera un déguisement dOrc Gueule-de-Dragon lorsque vous entrez dans la zone Aile-du-Néant et vous permettra de communiquer et de travailler pour les Gueules-de-Dragon stationné là-bas.\n\nMordenai vous enverra d\'abord à [npc=23139] avec un ensemble de faux papiers. L\'achèvement de cette quête débloque le début des quêtes Gueule-de-Dragon sur lesquelles vous travaillerez pour augmenter votre réputation Aile-du-Néant.\n\nLa plupart de ces quêtes seront journalières (ajoutée à la 2.1). Les quêtes journalières diffèrent des quêtes régulières car elles sont infiniment repérables, mais vous ne pouvez compléter chaque quête journalière qu\'une fois par jour et se limiter à 25 quêtes journalières par jour.\n[i]Remarque : De nouvelles quêtes seront débloquées après chaque niveau de réputation, et toutes les quêtes journalières des niveaux précédents seront toujours disponibles.[/i]\n\n[b][toggler id=Neutralcaché]Neutre[/toggler][/b]\n\n[div id=Neutralcaché] \nAprès avoir donné la [item=32469] à [npc=23139] pour compléter [quest=11013], votre première suite de quêtes sera disponible pour accéder au prochain niveau de réputation avec Aile-du-Néant.\n\nMor\'ghor vous indiquera daller voir le maître d\'uvre afin de commencer votre travail, et [npc=23141] se révélera comme un allié déguisé et vous proposera dautres quêtes.\nL\'une d\'entre elles est [quest=11049]. Les joueurs pourront trouver, avec un peu de chance (1% de loot), l[item=32506] sur presque toutes les créatures de lescarpement dAile-du-Néant et sur un [item=185881] ou un [item=185877].\nYarzill voudra aussi une trouvaille rare, l[item=185915], trouvée n\'importe où sur le rebord dAile-du-Néant et dans la forteresse Gueule-de-Dragon, coin sud-est de la vallée de dOmbrelune. Cette quête n\'est pas étiquetée comme journalière et peut donc être effectuée autant de fois que vous voulez, du moment que vous pouvez trouver des ufs. Cette quête nest pas comprise dans votre limite de quête journalière.\n\nAutres quêtes disponibles dès le début:\n[ul]\n[li][i][small]Journalière[/small][/i] - [quest=11018], [quest=11016], [quest=11017] Nest disponible que pour les joueurs qui possèdent la profession adaptée pour rassembler chaque élément.[/li]\n[li][i][small]Journalière[/small][/i] - [quest=11015] - Une quête de collecte simple ouverte à tous les joueurs indépendamment de leur profession.[/li] \n[li][i][small]Journalière[/small][/i] - [quest=11020] - Yarzill vous demandera de collecter des [item=32502]s et de les utiliser afin dempoisonner les péons qui travaillent pour rassembler des ressources pour Gueule-de-Dragon.[/li]\n[li][i][small]Journalière[/small][/i] - [quest=11035] - Vous devrez voler vers le coin nord-est de lescarpement dAile-du-Néant et vous positionner sur une des roches flottantes pour intercepter le [npc= 23188] et récupérer 10 x [item=32509].[/li]\n[/ul]\n[/div]\n[b][toggler id=Friendlyhidden]Amical[/toggler][/b]\n\n[div id=Friendlyhidden]\nMor\'ghor vous donnera un [item=32694] pour circuler avec votre nouveau rang parmi les Gueules-de-Dragon.\n[ul]\n[li][quest=11083] - [npc=23166] vous enverra tuer des bourbesangs qui sont stationné profondément dans les mines.[/li]\n[li][quest=11081] - Après avoir trouvé les [item=32726] dans un [item=32724], vous révélerez ce qui se passe réellement avec les bourbesangs dans la mine.[/li]\n[li][quest=11054] - [npc=23291] vous donnera vos propres [item=32680] pour garder les pétons Gueules-de-Dragon en ligne et travailler avec efficacité[/li]\n[li][i][small]Journalière[/small][/i] - [quest=11076] - La [npc=23149] vous demandera de vous aventurer dans les mines Ailes-du-Néant et de récupérer la cargaison contenue dans les chars de la mine qui est jetée au hasard dans l\'intérieur de la mine.[/li]\n[li][i][small]Journalière[/small][/i] - [quest=11077] - L\'un des [npc=23376] vous informera que des créatures plus profondes dans la mine interrompent la production et vous demandent de réduire leur nombre.[/li]\n[li][i][small]Journalière[/small][/i] - [quest=11055] - Cette quête humoristique commence chez le [npc=23291] après que vous lui apportiez le matériel requis. Vous pourrez survoler lescarpement Aile-du-Néant et lancer le Booterang à n\'importe quel [npc=23311] qui sy trouve autour des cris-taux.[/li]\n[/ul]\n[/div]\n[b][toggler id=Honorécaché]Honoré[/toggler][/b]\n\n[div id=Honorécaché]\nMor\'ghor vous donnera votre nouveau [item=32695], qui est maintenant utilisable n\'importe où, tant que vous êtes à l\'extérieur.\n[ul]\n[li][quest=11063] - Cette quête en six parties est une course aérienne contre les autres maîtres de vol Gueule-de-Dragon. Ils tenteront tous de vous renverser, vous et votre monture, avec des attaques aériennes habilement placées, vous devez rester visible et sur votre monture jusqu\'à leur atterrissage, si vous échouez, vous devez redémarrer la quête. Après avoir vaincu le dernier des six coureurs, vous recevrez un [item=32863], qui fonctionne exactement comme une [item=25653]. Les effets des deux bijoux ne sadditionnent pas.[/li]\n[li][quest=11089] Le [npc=23427] demandera un ensemble de matériaux pour créer un dispositif spécial pour détruire son frère et entraver les avancées de la légion dans l\'ouest de [zone=3518].[/li]\n[li][i][small]Journalière[/small][/i] - [quest=11086] - Mor\'ghor Vous enverra au Portal de Nagrand pour tuer 20 [url=?npcs=7&filter=na=ombremort] Agents Ombremort[/url]. Attention aux seigneurs, ils patrouillent dans la région et peuvent vous tuer dcoup de poing.[/li]\n[/ul]\n[/div]\n[b][toggler id=Révéréhidden]Révéré[/toggler][/b]\n\n[div id=Révéréhidden]\nMor\'ghor vous donnera votre nouveau [item=32864], le plus haut bijou.\n[ul]\n[li][url=?quests&filter=na=tuez%20les%20tous;minle=70;maxle=70] Tuez-les tous ![/url] - Mor\'ghor vous ordonnera de commencer l\'attaque la base d\'opérations de votre faction dans la vallée de Sombrelune. De toute évidence, vous n\'allez pas autoriser les Gueules-de-Dragon à attaquer vos alliés, alors vous informerez au leader approprié et débloquerez votre dernière quête journalière pour les Gueules-de-Dragon.[/li]\n[li][i][small]Journalière[/small][/i] [url=?quests&filter=na=le%20plus%20mortel%20des%20pièges]Le plus mortel des pièges[/url] - Les forces Gueules-de-Dragon vont attaquer la base des opérations. Apportez des alliés, car il s\'agit d\'une grande bataille.[/li]\n[/ul]\n[/div]\n[b][toggler id=Exaltécaché]Exalté[/toggler][/b]\n\n[div id=Exaltécaché]\nAprès de nombreux jours de travail, finalement le dénouement de la suite des quêtes Aile-du-Néant / Gueule-de-Dragon, vous dirigera à Mor\'ghor une dernière fois, qui vous informera que vous serez promu par [npc=22917] lui-même.\nSans gâcher les événements qui s\'ensuivent, vous vous retrouverez à Shattrath avec une sélection de montures épiques Aile-du-Néant. Vous pouvez en choisir un gratuitement, et si vous décidez d\'une couleur différente plus tard, vous pouvez acheter un autre drake chez [npc=23489] dans le camp de Gueule-de-Dragon pour 200 or.\n[/div]'),(8,1031,2,NULL,0,2,'Les [b]Gardes-ciel sha\'tari[/b] sont les gardiens aériens de [zone=3703], défendant la capitale des assaillants dans les collines ainsi que la lutte contre les Arakkoas de Terokk dans les sommets de Skettis. [faction=935] dirigent les gardes-ciel shatari.\nIls ont deux avant-postes, l\'un au nord des montages de Skettis et un près d[faction=1038]. Les joueurs commencent avec une réputation neutre chez les Gardes-ciel sha\'tari.\n\n[h3]Réputation[/h3]\n\n[b]Quêtes journalières[/b]\n[ul]\n[li][quest=11008] - [npc=23048] vous accordera un paquet d\'explosifs pour détruire les oeufs qui reposent au sommet des structures de Skettis. [/li]\n[li][quest=11085] - Le [npc=23383] peut être trouvé au sommet de certaines structures, les joueurs l\'escorteront pour la réputation, l\'or et un choix entre deux potions : [item=28100] ou [item=28101].[/li]\n[li][quest=11065] - [npc=23335] vous informera que les bombardements, de lavant-poste de la garde-ciel, ont coûté la vie de leurs montures et vous demandent de rassembler des Raies de léther pour compléter leurs forces aériennes.[/li]\n[li][quest=11010] - [npc=23120] vous demande de détruire les munitions pour les canons de la Légion afin que les gardes-ciel puissent continuer leur travail.[/li]\n[li][quest=11004] - Après avoir recueilli 6 [item=32388], [npc=23042] fera une potion qui permettra de voir l\'arakkoa le plus puissant, tel que [npc=23066].[i][small] Note : cette quête n\'est pas une quête journalière, mais peut être répété autant de fois que nécessaire. [/small][/i][/li]\n[/ul]\n\n[b]Créatures[/b]\n\n[ul]\n[li][npc=21804] - 5 points de réputation, jusqu\'à la fin de Révéré[/li]\n[li][url=?npcs&filter=na=skettis+-kaliri+-assassin;minle=70] Tous les Arakkoa de Skettis[/url] - 10 points de réputation.[/li]\n[li][npc=23029] - 30 points de réputation.[/li]\n[/ul]'),(NULL,NULL,0,'new',0,2,'Any user can write a guide and then share it with the community. Before a guide will be available to the public, it will be put in a queue where it can be approved or rejected by the staff. We suggest that you make sure your guide is complete before you put it through this process. A complete guide will generally be thorough, 100% accurate for World of Warcraft\'s current build, and include details such as images.\n\n[h3]Tips For Creating Quality Guides[/h3]\n\n[ul][li][b]Use [url=?help=markup-guide]Aowow\'s BBCode[/url].[/b][/li]\n[li][b]Choose the correct category.[/b] Guides placed in the wrong category risk being rejected. Don\'t see your category? Email [feedback]![/li]\n[li][b]Always submit only complete guides.[/b] You can save in-progress ones indefinitely so you won\'t risk losing them.[/li]\n[li][b]Make sure it\'s on a unique topic with unique advice.[/b] If someone has already covered your topic, make sure that your guide offers something different and/or better advice or else it may be downvoted by our community.[/li]\n[li][b]Extremely short guides may be better off as a comment.[/b] Though overall there is no predetermined length for a good guide.[/li]\n[li][b]We do not tolerate plagiarism in any form.[/b] Make sure to include credits to other sources and a hyperlink if you use their images or otherwise.[/li][/ul]'),(NULL,NULL,0,'edit',0,2,'Any user can write a guide and then share it with the community. Before a guide will be available to the public, it will be put in a queue where it can be approved or rejected by the staff. We suggest that you make sure your guide is complete before you put it through this process. A complete guide will generally be thorough, 100% accurate for World of Warcraft\'s current build, and include details such as images.\n\n[h3]Tips For Creating Quality Guides[/h3]\n\n[ul][li][b]Use [url=?help=markup-guide]Aowow\'s BBCode[/url].[/b][/li]\n[li][b]Choose the correct category.[/b] Guides placed in the wrong category risk being rejected. Don\'t see your category? Email [feedback]![/li]\n[li][b]Always submit only complete guides.[/b] You can save in-progress ones indefinitely so you won\'t risk losing them.[/li]\n[li][b]Make sure it\'s on a unique topic with unique advice.[/b] If someone has already covered your topic, make sure that your guide offers something different and/or better advice or else it may be downvoted by our community.[/li]\n[li][b]Extremely short guides may be better off as a comment.[/b] Though overall there is no predetermined length for a good guide.[/li]\n[li][b]We do not tolerate plagiarism in any form.[/b] Make sure to include credits to other sources and a hyperlink if you use their images or otherwise.[/li][/ul]'),(13,1,3,NULL,0,2,'[b][color=c1]Krieger[/color][/b] sind eine sehr mächtige Klasse, die sowohl tanken als auch im Nahkampf erheblichen Schaden anrichten kann. Der [icon name=ability_warrior_defensivestance][url=?spells=7.1.257]Schutz[/url][/icon]-Talentbaum des Kriegers enthält viele Talente, um seine Überlebensfähigkeit zu verbessern und Bedrohung gegenüber Monstern zu erzeugen. Schutz-Krieger sind eine der wichtigsten Tank-Klassen des Spiels.\n\nAußerdem verfügen Krieger über zwei schadensorientierte Talentbäume - [icon name=ability_rogue_eviscerate][url=?spells=7.1.26]Waffen[/url][/icon] und [icon name=ability_warrior_innerrage][url=?spells=7.1.256]Furor[/url][/icon]. Der Furor-Talentbaum enthält das Talent [spell=46917], das es dem Krieger erlaubt, zwei Zweihandwaffen gleichzeitig zu führen! Krieger sind in der Lage, mit Fähigkeiten wie [spell=845], [spell=1680] und [spell=46924] starken Flächenschaden im Nahkampf zu verursachen. Ein Krieger kämpft in einer bestimmten [i]Haltung[/i], die ihm Boni und Zugang zu verschiedenen Fähigkeiten gewährt. Zu Beginn verfügen Krieger nur über die [spell=2457], erlernen aber mit Level 10 [spell=71] und mit Level 30 [spell=2458]. Die Verteidigungshaltung wird zum Tanken, die Kampfhaltung oder Berserkerhaltung für erheblichen Nahkampfschaden verwendet.\n\n[ul][li]Alle Krieger können ihren Schlachtzug oder ihre Gruppe mit einem [spell=6673] oder [spell=469] verstärken. Furor-Krieger besitzen den passiven Stärkungszauber [spell=29801], der die Chance auf kritische Treffer im Nah- und Fernkampf für ihre Verbündeten deutlich erhöht.[/li][li]Krieger haben zahlreiche nützliche Fähigkeiten, um schnell an ihr Ziel zu gelangen! Alle Krieger können [spell=100] oder [spell=20252] benutzen, um einen Gegner zu erreichen. Zudem können sie schnell [spell=3411], um ein befreundetes Ziel vor einem Angriff zu schützen.[/li][/ul]'),(13,2,3,NULL,0,2,'[b][color=c2]Paladine[/color][/b] unterstützen ihre Verbündeten mit heiligen Auren und Segen, um sie vor Schaden zu bewahren und ihre Kräfte zu stärken. Sie tragen Plattenrüstungen und können in den härtesten Schlachten verheerenden Schlägen standhalten, während sie ihre Verwundeten heilen und die Gefallenen wiederbeleben. Im Kampf können sie mächtige Zweihandwaffen führen, ihre Feinde betäuben, Untote und Dämonen vernichten und ihre Feinde mit heiliger Vergeltung richten. Paladine sind eine defensive Klasse, die in erster Linie darauf ausgelegt ist, ihre Gegner zu überdauern.\n\nDer Paladin ist hauptsächlich ein Nahkämpfer und in geringem Maße Zauberer, der aufgrund seiner [url=?spells=7.2&filter=cr=109:12;crs=10:1;crv=0:0]Heilzauber[/url], [url=?spells=7.2&filter=na=Segen]Segen[/url] und anderen Fähigkeiten sehr nützlich für die Gruppe ist. Sie können eine aktive [url=?spells=7.2&filter=na=Aura]Aura[/url] pro Paladin auf alle Gruppen- und Schlachtzugsmitglieder legen und bestimmte Segen für bestimmte Spieler verwenden. Dank ihrer zahlreichen defensiven Fähigkeiten vergessen Paladine einfach unglaublich oft zu sterben. Mit ihrer Fähigkeit [spell=25780] sind sie außerdem ausgezeichnete Tanks.\n\n[ul][li]Paladine können effektiv [icon name=spell_holy_holybolt][url=?spells=7.2.594]heilen[/url][/icon], [icon name=spell_holy_devotionaura][url=?spells=7.2.267]tanken[/url][/icon] und im Nahkampf [icon name=spell_holy_auraoflight][url=?spells=7.2.184]Schaden[/url][/icon] verursachen.[/li][li]Sie besitzen eine große Auswahl an Segen, Auren und anderen Verstärkungszaubern.[/li][li]Der Paladin ist die einzige Klasse mit Zugang zu einem echten Unverwundbarkeitszauber: [spell=642].[/li][/ul]'),(13,3,3,NULL,0,2,'[b][color=c3]Jäger[/color][/b] sind eine besonders einzigartige Klasse in World of Warcraft. Sie sind die einzigen nicht-magischen Fernkämpfer, die mit Bögen und Gewehren kämpfen. Jäger verfügen über verschiedene Arten von Schüssen und Bissen zur Schwächung ihrer Gegner und können [url=?spells=7.3&filter=cr=4;crs=1;crv=0;na=Falle]Fallen[/url] legen, um Schaden zu verursachen oder den Gegner auf andere Weise zu verlangsamen oder kampfunfähig zu machen.\n\nJäger [icon name=ability_hunter_beasttaming][url=?spell=1515]zähmen Wildtiere[/url][/icon], damit diese sie als [url=?pets]Begleiter[/url] im Kampf unterstützen. Zwar sind Jäger nicht die einzige Klasse, die Begleiter einsetzen kann. Ihre Tierbegleiter sind aber insofern einzigartig, als jede Spezies einen [url=?petcalc]eigenen Talentbaum[/url] hat, den der Jäger nutzen kann, um Punkte auf verschiedene Fähigkeiten zu verteilen.\n\nDarüber hinaus hat jede Spezies eine einzigartige Spezialfähigkeit. Jäger können sich die begehrtesten Begleiter aufgrund ihres Aussehens oder ihrer Fähigkeiten aussuchen. Und wenn sie genug Talentpunkte in den Baum der [icon name=ability_hunter_beasttaming][url=?spells=7.3.50]Tierherrschaft[/url][/icon] investieren, können sie besondere, \"exotische\" Bestien zähmen, wie [url=?pet=46]Geisterbestien[/url] oder [url=?pet=39]Teufelssaurier[/url]!\n\n[ul][li]Jäger haben Zugriff auf 25 (32 als [icon name=ability_hunter_beastmastery][url=?spell=53270]Meister der Tierherrschaft[/url][/icon]) verschiedene Arten von Begleitern mit über 150 verschiedenen Erscheinungsbildern![/li][li]Jäger haben eine Reihe von überlebensorientierten Fähigkeiten, die sie einsetzen können, um potentiellen Gefahren zu entkommen oder ihnen auszuweichen, wie z.B. [spell=5384] und [spell=781].[/li][li]Auf das [icon name=ability_hunter_swiftstrike][url=?spells=7.3.51]Überleben[/url][/icon] spezialisierte Jäger können in ihrem Talentbaum Punkte in das Talent [icon name=ability_hunter_huntingparty][url=?spells=-2.3&filter=na=jagdgesellschaft rel=spell=53292]Jagdgesellschaft[/url][/icon] investieren, welches es ihnen ermöglicht, ihre Gruppen- und Schlachtzugsmitglieder mit dem Stärkungszauber [spell=57669] zu versorgen.[/li][/ul]'),(13,4,3,NULL,0,2,'[b][color=c4]Schurken[/color][/b] sind eine Nahkampfklasse, die Lederrüstungen trägt und ihren Feinden mit sehr schnellen Angriffen großen Schaden zufügen kann. Sie sind Meister der Verstohlenheit und des Meuchelns, die sich ungesehen an Feinden vorbeischleichen, aus den Schatten heraus zuschlagen und dann blitzschnell aus dem Kampf verschwinden.\n\nSie sind in der Lage, [url=?items=0.-3&filter=cr=152;crs=4;crv=0;ty=-3#0+1-2]Gifte[/url] einzusetzen, um ihre Gegner zu verkrüppeln und sie so im Kampf massiv zu schwächen. Schurken verfügen über ein mächtiges Arsenal an Fähigkeiten, von denen viele dadurch verstärkt werden, dass sie in [spell=1784] schleichen und ihre Opfer kampfunfähig machen können.\n\nSchurken können sich auf drei unterschiedliche Kampfstile mithilfe ihrer Talentbäume Meucheln, Kampf und Täuschung spezialisieren.\n\nAuf das [icon name=ability_rogue_eviscerate][url=?spells=7.4.253]Meucheln[/url][/icon] spezialisierte Schurken sind [icon name=ability_creature_poison_06][url=?spells=-2&filter=na=meister+der+gifte rel=spell=58410]Meister der Gifte[/url][/icon] und [icon name=ability_rogue_disembowel][url=?spell=57993]vergiften[/url][/icon] ihre Gegner mit schnellen Dolchen, die mit [icon name=ability_rogue_feigndeath][url=?spells=-2.4.253&filter=na=Üble+Gifte rel=spell=16515]üblen[/url][/icon] und [icon name=ability_poisons][url=?spells=-2.4.253&filter=na=Verbesserte+Gifte rel=spell=14117]verbesserten[/url][/icon] Giften versehen sind.\n\nAuf den [icon name=ability_backstab][url=?spells=7.4.38]Kampf[/url][/icon] spezialisierte Schurken können den Umgang mit [icon name=inv_sword_27][url=?spells=-2&filter=na=Niedermetzeln rel=spell=13964]Axt und Schwert[/url][/icon] oder [icon name=inv_mace_01][url=?spells=-2&filter=na=Streitkolben-Spezialisierung;cl=4 rel=spell=13803]Streitkolben[/url][/icon] meistern und haben mithilfe ihrer Talente auch in langwierigen Kämpfen eine verbesserte Energiezufuhr, um zuverlässig ihre Angriffscombos durchzuführen.\n\nAuf die [icon name=ability_stealth][url=?spells=7.4.39]Täuschung[/url][/icon] spezialisierte Schurken besitzen Fähigkeiten, die unvorhergesehene Aktionen ermöglichen. So können sie dank [spell=51713] etwa kurzzeitig Fähigkeiten nutzen, die eigentlich nur aus der Verstohlenheit heraus nutzbar wären, mit [spell=36554] plötzlich hinter einem Gegner auftauchen oder mit [icon name=ability_rogue_cheatdeath][url=?spells=-2.4.39&filter=cr=15;crs=0;crv=ability_rogue_cheatdeath rel=spell=31230]Von der Schippe springen[/url][/icon] einen sicheren Todesstoß überleben.'),(13,5,3,NULL,0,2,'[b][color=c5]Priester[/color][/b] gelten allgemein als eine der Standard-Heilerklassen in World of Warcraft, da sie über zwei Talentspezialisierungen zur effektiven Heilung verfügen.\n\nIhr [icon name=spell_holy_holybolt][url=?spells=7.5.56]Heilig[/url][/icon]-Talentbaum enthält Talente, die die Heilung auf ihre Verbündeten erheblich verstärken - einschließlich Zaubern, mit denen mehrere Spieler gleichzeitig geheilt werden können, wie z.B. [spell=48089].\nDer [icon name=spell_holy_wordfortitude][url=?spells=7.5.613]Disziplin[/url][/icon]-Talentbaum ist zwar auch in der Lage, eine beträchtliche Menge an Heilung zu bewirken, konzentriert sich aber in erster Linie auf die Schadensabsorption und -verminderung durch den Einsatz von [spell=48066] und [icon name=spell_holy_devineaegis][url=?spells=-2.5&filter=cr=15;crs=0;crv=spell_holy_devineaegis rel=spell=47515]Göttliche Aegis[/url][/icon].\nPriester können außerdem mit ihren [icon name=spell_shadow_shadowwordpain][url=?spells=7.5.78]Schatten[/url][/icon]fähigkeiten sehr mächtigen Fernkampfschaden verursachen. Insbesondere wenn sie ihre [spell=15473] annehmen, erhöht sich ihr Schattenschaden erheblich, aber sie verlieren ihre Fähigkeit, Heiligzauber zu wirken.\n\n[ul][li]Der Disziplin-Talentbaum wird in der Regel zur Heilung verwendet, enthält aber auch einige Talente zur Erhöhung des Schadens des Priesters, wobei Schattenzauber und -fähigkeiten in erster Linie zur Verursachung von Fernkampfschaden verwendet werden sollten.[/li][li]Priester verfügen über einen der am meisten geschätzten Stärkungszauber im Spiel - [spell=48161], welcher allen befreundeten Mitspielern eine unverzichtbare Erhöhung ihrer Ausdauer gewährt. Außerdem können sie ihre Mitspieler mit [spell=48073] und [spell=48169] verstärken und mit einzigartigen Hymnen das [icon name=spell_holy_divinehymn][url=?spell=64843]Leben[/url][/icon] und [icon name=spell_holy_symbolofhope][url=?spell=64901]Mana[/url][/icon] ihres Schlachtzug signifikant wiederherstellen![/li][li]Schattenpriester unterstützen, zusätzlich zu ihrem Schaden, jeden Schlachtzug mit dem beliebten Stärkungszauber [spell=57669] zur Erhöhung der Manaregeneration und mit ihrer [spell=15286], die ihre gesamte Gruppe passiv heilt.[/li][/ul]'),(13,6,3,NULL,0,2,'Die [b][color=c6]Todesritter[/color][/b] wurden in der Erweiterung Wrath of the Lich King eingeführt und sind die erste Heldenklasse von World of Warcraft. Todesritter beginnen auf Stufe 55 in einer speziellen, instanzierten Zone, die für andere Klassen unzugänglich ist: [url=?maps=4298:511346]Acherus, die Schwarze Festung[/url] in der Scharlachroten Enklave der Östlichen Pestländer. Hier erhalten sie ihre Talentpunkte durch Questbelohnungen und bekommen sogar ein besonderes beschworenes Reittier: das [spell=48778]!\n\nTodesritter haben mehrere sehr starke Möglichkeiten zur Schadensverursachung, da jeder ihrer Talentbäume es erlaubt mit einer Vielfalt an Nahkampffähigkeiten, Zaubern und Schaden-über-Zeit verursachenden Krankheiten überragende Leistung zu erbringen. Sie sind auch sehr fähige Tanks, wobei sowohl ihr Blut- als auch ihr Frost-Talentbaum einzigartige Optionen bietet. [icon name=spell_deathknight_bloodpresence][url=?spells=7.6.770]Blut[/url][/icon] bietet mehr Selbstheilungsfähigkeiten, [icon name=spell_deathknight_frostpresence][url=?spells=7.6.771]Frost[/url][/icon] bietet erhebliche Schadensminderung und starken Flächenschaden.\n\nTodesritter kämpfen mit einem besonderen Verstärkungszauber, der [url=?spells=7&filter=na=präsenz]Präsenz[/url] genannt wird (ähnlich wie die Haltungen eines Kriegers), der ihnen besondere Boni für ihre Rollen verleiht. Todesritter verwenden ein einzigartiges Ressourcensystem, bei dem die meisten Zauber entweder [url=?spells=7.6&filter=cr=45;crs=10;crv=0#50+1+13+3]Runen[/url] kosten, die während des Kampfes wieder aufgefüllt werden, oder [url=?spells=7.6&filter=cr=45;crs=11;crv=0]Runenmacht[/url], die durch verschiedene Fähigkeiten erzeugt werden kann.\n\n[ul][li]Auf [icon name=spell_deathknight_unholypresence][url=?spells=7.6.772]Unheilig[/url][/icon] spezialisierte Todesritter können sich in [spell=52143] spezialisieren, was ihren beschworenen Ghul-Wächter zu einem permanenten Begleiter macht, der sie im Kampf unterstützt![/li][li]Die Klasse der Todesritter verfügt über eine eigene spezielle Waffenverzauberungsfähigkeit namens [spell=53428], die herkömmliche Waffenverzauberungen überflüssig macht.[/li][li]Todesritter sind eine Schadensklasse, die ihren Schaden sowohl durch Nahkampffähigkeiten als auch durch Zauber verursacht![/li][/ul]'),(13,7,3,NULL,0,2,'[b][color=c7]Schamanen[/color][/b] beherrschen Elementar- und Naturmagie und bringen einer (Schlachtzugs-) Gruppe die größte Vielfalt an potenziellen Stärkungszaubern in Form von [url=?spells=7&filter=na=Totem;cl=7]Totems[/url]. Ein Schamane kann für jedes Element - Erde, Feuer, Luft und Wasser - ein Totem beschwören, welches zu seinen Füßen erscheint und allen Mitgliedern seiner (Schlachtzugs-) Gruppe in Reichweite einen Stärkungszauber verleiht. Einige Totems, insbesondere Feuer-Totems, fügen Gegnern auch Schaden zu. Der Trick beim Spielen jeder Art von Schamanen besteht darin, zu wissen, welche Totems in welcher Situation beschworen werden müssen, um den verursachten Schaden und die Überlebensfähigkeit ihrer Gruppe zu maximieren.\n\nSchamanen sind in erster Linie Zauberer, wobei ein auf [icon name=spell_nature_lightningshield][url=?spells=7.7.373]Verstärkung[/url][/icon] spezialisierter Schamane Schaden in Nahkampfreichweite verursacht. Ein solcher Schamane erlernt das Führen zweier Waffen durch [spell=30798] und kann mit [spell=51533] zwei Schattenwölfe zur Unterstützung im Kampf beschwören. Obwohl sie hauptsächlich im Nahkampf eingesetzt werden, können auf Verstärkung spezialisierte Schamanen dennoch einen gewissen Nutzen aus ihrer Zaubermacht ziehen und spontane [icon name=spell_nature_lightning][url=?spell=49238]Blitzschläge[/url][/icon] oder [url=?spells=7&filter=cr=109:12:14;crs=10:1:5;crv=0:0:60000;cl=7]Heilungen[/url] durch [icon name=spell_shaman_maelstromweapon][url=?spells=-2&filter=na=waffe+des+mahlstroms rel=spell=51532]Waffe des Mahlstroms[/url][/icon] wirken.\n\nAuf [icon name=spell_nature_lightning][url=?spells=7.7.375]Elementarkampf[/url][/icon] spezialisierte Schamanen wirken Feuer- und Blitzzauber auf Distanz und verursachen so großen Schaden. Sie können Gegner durch [spell=59159] zurückstoßen und mit [icon name=spell_shaman_stormearthfire][url=?spells=-2&filter=na=sturm%2C+erde+und+feuer rel=spell=51486]Sturm, Erde und Feuer[/url][/icon] alle Feinde in einem Gebiet festwurzeln. Außerdem gewähren sie durch [spell=57722] und [icon name=spell_shaman_elementaloath][url=?spells=-2&filter=na=Elementarer+Schwur rel=spell=51470]Elementarer Schwur[/url][/icon] begehrte Stärkungszauber für Zauberer ihres Schlachtzugs.\n\nEin auf [icon name=spell_nature_magicimmunity][url=?spells=7.7.374]Wiederherstellung[/url][/icon] spezialisierter Schamane erhält verbesserte Heilzauber und kann ein ausgezeichneter Schlachtzugs- oder Tankheiler sein. Sie sind bekannt für ihre mächtige Fähigkeit [spell=55459] und dafür, dass sie ein [spell=16190] zur Verfügung stellen, welches der Gruppe hilft Mana wiederherzustellen. Sie erhalten außerdem ein mächtiges [spell=49284], können mit [spell=51886] Flüche entfernen und verfügen durch [spell=61301] über einen Spontanheilungseffekt, der zusätzlich eine Heilung über Zeit verursacht.\n\n[ul][li]Es gibt über zwanzig verschiedene Totems, die ein Schamane erlernen kann![/li][li]Schamanen der Horde können [spell=2825] und Schamanen der Allianz [spell=32182] wirken, wodurch der verursachte Schaden und die gewirkte Heilung der gesamten Gruppe erhöht wird. Dieser Stärkungszauber ist einzigartig und in jeder Schlachtzugsgruppe sehr begehrt.[/li][li]Ein Schamane kann sich ab Stufe 16 in einen [spell=2645] verwandeln und dies mit dem Talent [spell=16287] sogar als Spontanzauber wirken. Dieser Zauber kann im Kampf eingesetzt werden, aber nicht in geschlossenen Räumen.[/li][li]Schamanen können immer nur einen Elementarschild - [spell=49281] oder [spell=57960] - gleichzeitig benutzen. Auf Wiederherstellung spezialisierte Schamanen können zudem [spell=49284] auf einen anderen Spieler wirken.[/li][/ul]'),(13,8,3,NULL,0,2,'[b][color=c8]Magier[/color][/b] bändigen die Elemente Feuer, Frost und Arkan, um ihre Feinde zu vernichten oder unter Kontrolle zu halten. Dazu besitzen sie ein Arsenal voller Zauber zu unterschiedlichen Zwecken.\nStärkungszauber, [icon name=ability_mage_conjurefoodrank10][url=?spell=42956]herbeigezauberte Erfrischungen[/url][/icon] oder arkane [url=?spells=7&filter=na=Portal]Portale[/url] zur schnellen Weltreise in ferne Länder machen einen Magier zu einem idealen Weggefährten.\nUnd wenn man eine Klasse sucht, die Gegner in eine Welt des Schmerzes einführt, ist der Magier eine gute Wahl. Ihren Gegnern können Magier mit verschiedensten Schwächungszaubern die Bedingungen eines jeden Kampfes diktieren, mit Elementarblitzen massiven Schaden aus der Ferne anrichten, oder Zerstörung in einem großen Wirkungsbereich niederregnen lassen.\n\nAuf [icon name=spell_holy_magicalsentry][url=?spells=7.8.237]Arkan[/url][/icon] spezialisierte Magier haben das Potenzial, mit [icon name=spell_arcane_blast][url=?spell=42897]Arkanschlägen[/url][/icon] und [icon name=ability_mage_missilebarrage][url=?spells=-2&filter=na=Geschosssalve rel=spell=54490]Salven[/url][/icon] an [icon name=spell_nature_starfall][url=?spell=42846]Arkanen Geschossen[/url][/icon] in kurzer Zeit enormen Schaden zu verursachen. Das Bändigen der reinen arkanen Mächte hat jedoch ihre Kehrseite: einem unerfahrenen Arkanmagier verzehrt es schon nach kurzer Zeit seine gesamten Kräfte.\n\nAuf [icon name=spell_fire_flamebolt][url=?spells=7.8.8]Feuer[/url][/icon] spezialisierte Magier verfallen durch kritische Treffer mit Feuerzaubern in [icon name=ability_mage_hotstreak][url=?spells=-2&filter=na=Kampfeshitze rel=spell=44448]Kampfeshitze[/url][/icon] und äschern so ihre Gegner mit verheerenden [icon name=spell_fire_fireball02][url=?spell=42891]Pyroschlägen[/url][/icon] ein. Zudem verwandeln sie ihre Gegner in [icon name=ability_mage_livingbomb][url=?spell=55360]Lebende Bomben[/url][/icon] und verursachen dadurch explosiven Flächenschaden.\n\n[icon name=spell_frost_frostbolt02][url=?spells=7.8.6]Frost[/url][/icon]magier können ihre Gegner [icon name=ability_mage_deepfreeze][url=?spell=44572]in Eis erstarren[/url][/icon] lassen. Ihre Spezialisierung auf Kälteeffekte erlaubt ihnen eine starke Kontrolle über ihre Gegner und erhöht dadurch ihre Überlebensfähigkeit enorm.\n\n[ul][li]Magier können Erfrischungen herbeizaubern, um die Gesundheit und das Mana ihrer Verbündeten wiederherzustellen.[/li][li]Sie sind die einzige Klasse, die Portale erschaffen kann, um andere Spieler zu transportieren. Sie können jedoch keine Spieler von einem entfernten Ort herbeirufen - das ist die Aufgabe eines [class=9]![/li][li]Der verursachte Fernkampfschaden von Magiern ist einer der höchsten im Spiel und macht sie zu einem unverzichtbaren Verbündeten in jedem Schlachtzug.[/li][/ul]'),(13,9,3,NULL,0,2,'[b][color=c9]Hexenmeister[/color][/b] sind Meister der dämonischen Künste. Gekleidet in Gewänder sind sie Meister im Wirken von [url=?spells=7&filter=cr=12;crs=1;crv=0;na=Fluch+der;cl=9]Flüchen[/url], dem Schleudern von Feuer- oder Schattenblitzen und der Beschwörung von [url=?spells=7&filter=cr=14;crs=6;crv=48018;na=beschwören;cl=9]Dämonen[/url] unter ihre Kontrolle zur Unterstützung im Kampf. Die Kombination ihrer Flüche und direkten Schadenszauber richten Verwüstung und Zerstörung an und machen Hexenmeister zu sehr gefürchteten Gegnern.\n\nNeben Mana als primäre Ressource können Hexenmeister Gegnern Teile ihrer [icon name=spell_shadow_haunting][url=?spell=47855]Seele stehlen[/url][/icon] und dadurch [item=6265] erzeugen. Seelensplitter ermöglichen mächtige rituelle Magie, etwa zur [icon name=spell_shadow_twilight][url=?spell=698]Beschwörung von anderen Spielern[/url][/icon] oder von [icon name=spell_shadow_shadesofdarkness][url=?spell=58887]Gesundheitssteinen[/url][/icon] mit heilenden Kräften. Insbesondere kann jedoch ein Hexenmeister mit ihnen die Seele eines Verbündeten in einem [icon name=spell_shadow_soulgem][url=?spell=47884]Seelenstein[/url][/icon] speichern, sodass dieser im Todesfall sich selbst wiederbeleben kann.\n\n[ul][li]Hexenmeister können durch ein Ritual der Beschwörung ein Portal erschaffen, um einen anderen Spieler an den Ort des Portals zu beschwören.[/li][li]Sie können Gesundheitssteine beschwören, die den Anwender heilen.[/li][li]Die Flüche eines Hexenmeisters können ihre Feinde schwächen oder ihnen Schaden zufügen.[/li][/ul]'),(13,11,3,NULL,0,2,'[b][color=c11]Druiden[/color][/b] sind die \"Alleskönner\"-Klasse in World of Warcraft - das heißt, sie können in einer Vielzahl von verschiedenen Rollen agieren und bieten daher einen der vielfältigsten Spielstile. Durch das [i]Annehmen der Gestalt von verschiedenen Kreaturen[/i] kann der Druide heilen, Schaden im Nah- und Fernkampf verursachen oder als Tank agieren. Mit steigenden Stufen kann der Druide neue, immer mächtigere Gestaltwandlungen erlernen, um sich in eine Kreatur passend zu seiner Rolle zu verwandeln.\n\nAuf niedrigeren Stufen wird ein Druide in seiner humanoiden Gestalt heilen oder im Fernkampf Schaden verursachen. Auf späteren Stufen jedoch erhalten Druiden durch die spezialisierten Talentbäume Zugang zu zwei besonderen Gestalten für jede unterschiedliche Rolle.\n\nAuf [icon name=spell_nature_healingtouch][url=?spells=7.11.573]Wiederherstellung[/url][/icon] spezialisierte Druiden erlernen den [spell=33891], der die Manakosten ihrer Heilzauber reduziert und jegliche Heilung auf ihre Verbündeten verstärkt.\nAuf [icon name=spell_nature_starfall][url=?spells=7.11.574]Gleichgewicht[/url][/icon] spezialisierte Druiden verursachen Schaden im Fernkampf und erlernen die [spell=24858], die ihre Rüstung sowie die Chance auf kritische Treffer mit Zaubern bei ihnen und ihren Verbündeten erhöht.\nEs gibt auch zwei Druidenformen für den [icon name=ability_racial_bearform][url=?spells=7.11.134]Wilden Kampf[/url][/icon]. Zum einen die mächtige [spell=5487] (und [spell=9634] ab einer höheren Stufe) - eine auf das Tanken ausgelegte Gestalt, die zusätzliche Rüstung, Gesundheit und Zugang zu einem Arsenal von Fähigkeiten zur Erhöhung der Bedrohung und Schadensverminderung gewährt. Zum anderen die schurkenähnliche [spell=768], die erheblichen Nahkampfschaden verursachen kann.\n\n[ul][li]Druiden erlernen ihre verschiedenen Gestalten durch das Abschließen von Quests oder durch Training. Einige Gestalten können nur durch Talente erlernt werden.[/li][li]Es gibt einige Gestalten, die alle Druiden erlernen können. Die Bärengestalt erhält man ab Stufe 10, die [spell=1066] und [spell=783] ab Stufe 16, die Katzengestalt ab Stufe 20 und die Terrorbärengestalt ab Stufe 40.[/li][li]Druiden haben sogar ihre eigene fliegende Reisegestalt: die [spell=33943] kann ab Stufe 60 und die [spell=40120] ab Stufe 71 erlernt werden, sofern der Spieler bereits [icon name=spell_nature_swiftness][url=?spell=34093]Gekonntes Reiten[/url][/icon] erlernt hat.[/li][li]Einige Druidengestalten können nur über Talente erlernt werden - die Mondkingestalt kann ab Stufe 40 erlernt werden, wenn ein Spieler viele Talentpunkte im Gleichgewicht-Talentbaum verteilt, und Baum des Lebens ab Stufe 50, wenn er viele Talentpunkte im Wiederherstellung-Talentbaum verteilt.[/li][li]Druiden haben ihre eigene, klassenspezifische [icon name=spell_arcane_teleportmoonglade][url=?spell=18960]Teleportationsfähigkeit[/url][/icon], die es ihnen erlaubt, zur [zone=493] zu reisen - praktisch, wenn sie trainieren müssen![/li][li]Druiden in (Terror-) Bärengestalt oder Katzengestalt schwingen zur Verursachung von Nahkampfschaden keine Waffen. Stattdessen erhalten sie einen speziellen Wert für jede ausgerüstete Nahkampfwaffe: die \"Angriffskraft in Tiergestalt\". Dieser Wert ist eine Umwandlung des \"Schaden pro Sekunde\"-Wertes einer Waffe in einen Wert, der Angriffskraft verleiht und den verursachten Schaden des Druiden in Katzen- oder (Terror-) Bärengestalt beeinflusst.[/li][/ul]');
+/*!40000 ALTER TABLE `aowow_articles` ENABLE KEYS */;
+UNLOCK TABLES;
+
+/*!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 */;
+/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;
+
+-- Dump completed on 2025-09-22 23:29:16
diff --git a/setup/sql/04-db_optional_mysql_only.sql b/setup/sql/04-db_optional_mysql_only.sql
new file mode 100644
index 00000000..80561cb0
--- /dev/null
+++ b/setup/sql/04-db_optional_mysql_only.sql
@@ -0,0 +1,5 @@
+ALTER TABLE `aowow_creature` ADD FULLTEXT `idx_ft_name4` (`name_loc4`) WITH PARSER ngram;
+ALTER TABLE `aowow_items` ADD FULLTEXT `idx_ft_name4` (`name_loc4`) WITH PARSER ngram;
+ALTER TABLE `aowow_objects` ADD FULLTEXT `idx_ft_name4` (`name_loc4`) WITH PARSER ngram;
+ALTER TABLE `aowow_quests` ADD FULLTEXT `idx_ft_name4` (`name_loc4`) WITH PARSER ngram;
+ALTER TABLE `aowow_spell` ADD FULLTEXT `idx_ft_name4` (`name_loc4`) WITH PARSER ngram;
diff --git a/setup/sql/updates/1648222152_01.sql b/setup/sql/updates/1648222152_01.sql
new file mode 100644
index 00000000..e4daa30e
--- /dev/null
+++ b/setup/sql/updates/1648222152_01.sql
@@ -0,0 +1 @@
+ALTER TABLE aowow_mails ADD cuFlags INT UNSIGNED DEFAULT 0 NOT NULL AFTER id;
diff --git a/setup/sql/updates/1679660926_01.sql b/setup/sql/updates/1679660926_01.sql
new file mode 100644
index 00000000..5b3fe754
--- /dev/null
+++ b/setup/sql/updates/1679660926_01.sql
@@ -0,0 +1 @@
+UPDATE `aowow_dbversion` SET `build` = CONCAT(IFNULL(`build`, ''), ' talentIcons');
diff --git a/setup/sql/updates/1679679565_01.sql b/setup/sql/updates/1679679565_01.sql
new file mode 100644
index 00000000..391f0ab8
--- /dev/null
+++ b/setup/sql/updates/1679679565_01.sql
@@ -0,0 +1 @@
+UPDATE `aowow_dbversion` SET `sql` = CONCAT(IFNULL(`sql`, ''), ' spell');
diff --git a/setup/sql/updates/1680717296_01.sql b/setup/sql/updates/1680717296_01.sql
new file mode 100644
index 00000000..0ef56225
--- /dev/null
+++ b/setup/sql/updates/1680717296_01.sql
@@ -0,0 +1 @@
+UPDATE `aowow_dbversion` SET `build` = CONCAT(IFNULL(`build`, ''), ' power');
diff --git a/setup/sql/updates/1682012750_01.sql b/setup/sql/updates/1682012750_01.sql
new file mode 100644
index 00000000..1f28e1bf
--- /dev/null
+++ b/setup/sql/updates/1682012750_01.sql
@@ -0,0 +1 @@
+UPDATE `aowow_dbversion` SET `build` = CONCAT(IFNULL(`build`, ''), ' simpleImg');
diff --git a/setup/updates/1543271379_01.sql b/setup/sql/updates/1683672833_01.sql
similarity index 100%
rename from setup/updates/1543271379_01.sql
rename to setup/sql/updates/1683672833_01.sql
diff --git a/setup/sql/updates/1683928829_01.sql b/setup/sql/updates/1683928829_01.sql
new file mode 100644
index 00000000..6d163b57
--- /dev/null
+++ b/setup/sql/updates/1683928829_01.sql
@@ -0,0 +1 @@
+UPDATE `aowow_dbversion` SET `build` = CONCAT(IFNULL(`build`, ''), ' profiler');
diff --git a/setup/sql/updates/1683979752_01.sql b/setup/sql/updates/1683979752_01.sql
new file mode 100644
index 00000000..9d975603
--- /dev/null
+++ b/setup/sql/updates/1683979752_01.sql
@@ -0,0 +1 @@
+UPDATE `aowow_dbversion` SET `build` = CONCAT(IFNULL(`build`, ''), ' statistics');
diff --git a/setup/sql/updates/1684620409_01.sql b/setup/sql/updates/1684620409_01.sql
new file mode 100644
index 00000000..0eb594dd
--- /dev/null
+++ b/setup/sql/updates/1684620409_01.sql
@@ -0,0 +1,2 @@
+ALTER TABLE `aowow_source`
+ ADD COLUMN `moreMask` mediumint(9) unsigned DEFAULT NULL AFTER `moreZoneId`;
diff --git a/setup/sql/updates/1684620409_02.sql b/setup/sql/updates/1684620409_02.sql
new file mode 100644
index 00000000..bef0e1a9
--- /dev/null
+++ b/setup/sql/updates/1684620409_02.sql
@@ -0,0 +1 @@
+UPDATE `aowow_dbversion` SET `sql` = CONCAT(IFNULL(`sql`, ''), ' source');
diff --git a/setup/sql/updates/1684793469_01.sql b/setup/sql/updates/1684793469_01.sql
new file mode 100644
index 00000000..e6f2ad91
--- /dev/null
+++ b/setup/sql/updates/1684793469_01.sql
@@ -0,0 +1,71 @@
+REPLACE INTO `aowow_setup_custom_data` VALUES
+ ("spell", 9787, "reqSpellId", 9787, "Weaponsmith - requires itself"),
+ ("spell", 9788, "reqSpellId", 9788, "Armorsmith - requires itself"),
+ ("spell", 10656, "reqSpellId", 10656, "Dragonscale Leatherworking - requires itself"),
+ ("spell", 10658, "reqSpellId", 10658, "Elemental Leatherworking - requires itself"),
+ ("spell", 10660, "reqSpellId", 10660, "Tribal Leatherworking - requires itself"),
+ ("spell", 17039, "reqSpellId", 17039, "Master Swordsmith - requires itself"),
+ ("spell", 17040, "reqSpellId", 17040, "Master Hammersmith - requires itself"),
+ ("spell", 17041, "reqSpellId", 17041, "Master Axesmith - requires itself"),
+ ("spell", 20219, "reqSpellId", 20219, "Gnomish Engineer - requires itself"),
+ ("spell", 20222, "reqSpellId", 20222, "Goblin Engineer - requires itself"),
+ ("spell", 26797, "reqSpellId", 26797, "Spellfire Tailoring - requires itself"),
+ ("spell", 26798, "reqSpellId", 26798, "Mooncloth Tailoring - requires itself"),
+ ("spell", 26801, "reqSpellId", 26801, "Shadoweave Tailoring - requires itself"),
+ ("spell", 379, "cuFLags", 1073741824, "Earth Shield - hide"),
+ ("spell", 17567, "cuFLags", 1073741824, "Summon Blood Parrot - hide"),
+ ("spell", 19483, "cuFLags", 1073741824, "Immolation - hide"),
+ ("spell", 20154, "cuFLags", 1073741824, "Seal of Righteousness - hide"),
+ ("spell", 21169, "cuFLags", 1073741824, "Reincarnation - hide"),
+ ("spell", 22845, "cuFLags", 1073741824, "Frenzied Regeneration - hide"),
+ ("spell", 23885, "cuFLags", 1073741824, "Bloodthirst - hide"),
+ ("spell", 27813, "cuFLags", 1073741824, "Blessed Recovery - hide"),
+ ("spell", 27817, "cuFLags", 1073741824, "Blessed Recovery - hide"),
+ ("spell", 27818, "cuFLags", 1073741824, "Blessed Recovery - hide"),
+ ("spell", 29442, "cuFLags", 1073741824, "Magic Absorption - hide"),
+ ("spell", 29841, "cuFLags", 1073741824, "Second Wind - hide"),
+ ("spell", 29842, "cuFLags", 1073741824, "Second Wind - hide"),
+ ("spell", 29886, "cuFLags", 1073741824, "Create Soulwell - hide"),
+ ("spell", 30708, "cuFLags", 1073741824, "Totem of Wrath - hide"),
+ ("spell", 30874, "cuFLags", 1073741824, "Gift of the Water Spirit - hide"),
+ ("spell", 31643, "cuFLags", 1073741824, "Blazing Speed - hide"),
+ ("spell", 32841, "cuFLags", 1073741824, "Mass Resurrection - hide"),
+ ("spell", 34919, "cuFLags", 1073741824, "Vampiric Touch - hide"),
+ ("spell", 44450, "cuFLags", 1073741824, "Burnout - hide"),
+ ("spell", 47633, "cuFLags", 1073741824, "Death Coil - hide"),
+ ("spell", 48954, "cuFLags", 1073741824, "Swift Zhevra - hide"),
+ ("spell", 49575, "cuFLags", 1073741824, "Death Grip - hide"),
+ ("spell", 50536, "cuFLags", 1073741824, "Unholy Blight - hide"),
+ ("spell", 52374, "cuFLags", 1073741824, "Blood Strike - hide"),
+ ("spell", 56816, "cuFLags", 1073741824, "Rune Strike - hide"),
+ ("spell", 58427, "cuFLags", 1073741824, "Overkill - hide"),
+ ("spell", 58889, "cuFLags", 1073741824, "Create Soulwell - hide"),
+ ("spell", 64380, "cuFLags", 1073741824, "Shattering Throw - hide"),
+ ("spell", 66122, "cuFLags", 1073741824, "Magic Rooster - hide"),
+ ("spell", 66123, "cuFLags", 1073741824, "Magic Rooster - hide"),
+ ("spell", 66124, "cuFLags", 1073741824, "Magic Rooster - hide"),
+ ("spell", 66175, "cuFLags", 1073741824, "Macabre Marionette - hide"),
+ ("spell", 54910, "cuFLags", 1073741824, "Glyph of the Red Lynx - hide unused glyph"),
+ ("spell", 57231, "cuFLags", 1073741824, "Death Knight Glyph 25 - hide unused glyph"),
+ ("spell", 58166, "cuFLags", 1073741824, "Glyph of the Forest Lynx - hide unused glyph"),
+ ("spell", 58239, "cuFLags", 1073741824, "Glyph of the Penguin - hide unused glyph"),
+ ("spell", 58240, "cuFLags", 1073741824, "Glyph of the Bear Cub - hide unused glyph"),
+ ("spell", 58261, "cuFLags", 1073741824, "Glyph of the Arctic Wolf - hide unused glyph"),
+ ("spell", 58262, "cuFLags", 1073741824, "Glyph of the Black Wolf - hide unused glyph"),
+ ("spell", 60460, "cuFLags", 1073741824, "Glyph of Raise Dead - hide unused glyph"),
+ ("spell", 54910, "skillLine1", 0, "Glyph of the Red Lynx - hide unused glyph"),
+ ("spell", 57231, "skillLine1", 0, "Death Knight Glyph 25 - hide unused glyph"),
+ ("spell", 58166, "skillLine1", 0, "Glyph of the Forest Lynx - hide unused glyph"),
+ ("spell", 58239, "skillLine1", 0, "Glyph of the Penguin - hide unused glyph"),
+ ("spell", 58240, "skillLine1", 0, "Glyph of the Bear Cub - hide unused glyph"),
+ ("spell", 58261, "skillLine1", 0, "Glyph of the Arctic Wolf - hide unused glyph"),
+ ("spell", 58262, "skillLine1", 0, "Glyph of the Black Wolf - hide unused glyph"),
+ ("spell", 60460, "skillLine1", 0, "Glyph of Raise Dead - hide unused glyph"),
+ ("spell", 54910, "iconIdAlt", 0, "Glyph of the Red Lynx - hide unused glyph"),
+ ("spell", 57231, "iconIdAlt", 0, "Death Knight Glyph 25 - hide unused glyph"),
+ ("spell", 58166, "iconIdAlt", 0, "Glyph of the Forest Lynx - hide unused glyph"),
+ ("spell", 58239, "iconIdAlt", 0, "Glyph of the Penguin - hide unused glyph"),
+ ("spell", 58240, "iconIdAlt", 0, "Glyph of the Bear Cub - hide unused glyph"),
+ ("spell", 58261, "iconIdAlt", 0, "Glyph of the Arctic Wolf - hide unused glyph"),
+ ("spell", 58262, "iconIdAlt", 0, "Glyph of the Black Wolf - hide unused glyph"),
+ ("spell", 60460, "iconIdAlt", 0, "Glyph of Raise Dead - hide unused glyph");
diff --git a/setup/sql/updates/1684849475_01.sql b/setup/sql/updates/1684849475_01.sql
new file mode 100644
index 00000000..ce3abb2d
--- /dev/null
+++ b/setup/sql/updates/1684849475_01.sql
@@ -0,0 +1,2 @@
+ALTER TABLE `aowow_source`
+ MODIFY `typeId` mediumint(9) signed NOT NULL AFTER `type`;
diff --git a/setup/sql/updates/1691940877_01.sql b/setup/sql/updates/1691940877_01.sql
new file mode 100644
index 00000000..1d298b91
--- /dev/null
+++ b/setup/sql/updates/1691940877_01.sql
@@ -0,0 +1,49 @@
+DROP TABLE IF EXISTS `dbc_emotes`;
+DROP TABLE IF EXISTS `dbc_emotestext`;
+DROP TABLE IF EXISTS `dbc_emotestextdata`;
+
+DROP TABLE IF EXISTS `aowow_emotes`;
+CREATE TABLE `aowow_emotes` (
+ `id` SMALLINT(5) SIGNED NOT NULL,
+ `cmd` VARCHAR(35) COLLATE utf8mb4_unicode_ci NOT NULL,
+ `isAnimated` TINYINT(1) UNSIGNED NOT NULL,
+ `flags` SMALLINT(5) UNSIGNED NOT NULL,
+ `parentEmote` SMALLINT(5) UNSIGNED NOT NULL,
+ `soundId` SMALLINT(5) SIGNED NOT NULL,
+ `state` TINYINT(1) UNSIGNED NOT NULL,
+ `stateParam` TINYINT(1) UNSIGNED NOT NULL,
+ `cuFlags` INT(10) UNSIGNED NOT NULL,
+ `extToExt_loc0` VARCHAR(65) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
+ `extToExt_loc2` VARCHAR(113) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
+ `extToExt_loc3` VARCHAR(91) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
+ `extToExt_loc4` VARCHAR(71) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
+ `extToExt_loc6` VARCHAR(89) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
+ `extToExt_loc8` VARCHAR(123) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
+ `extToMe_loc0` VARCHAR(65) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
+ `extToMe_loc2` VARCHAR(113) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
+ `extToMe_loc3` VARCHAR(91) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
+ `extToMe_loc4` VARCHAR(71) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
+ `extToMe_loc6` VARCHAR(89) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
+ `extToMe_loc8` VARCHAR(123) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
+ `meToExt_loc0` VARCHAR(65) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
+ `meToExt_loc2` VARCHAR(113) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
+ `meToExt_loc3` VARCHAR(91) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
+ `meToExt_loc4` VARCHAR(71) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
+ `meToExt_loc6` VARCHAR(89) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
+ `meToExt_loc8` VARCHAR(123) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
+ `extToNone_loc0` VARCHAR(65) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
+ `extToNone_loc2` VARCHAR(113) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
+ `extToNone_loc3` VARCHAR(91) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
+ `extToNone_loc4` VARCHAR(71) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
+ `extToNone_loc6` VARCHAR(89) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
+ `extToNone_loc8` VARCHAR(123) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
+ `meToNone_loc0` VARCHAR(65) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
+ `meToNone_loc2` VARCHAR(113) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
+ `meToNone_loc3` VARCHAR(91) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
+ `meToNone_loc4` VARCHAR(71) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
+ `meToNone_loc6` VARCHAR(89) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
+ `meToNone_loc8` VARCHAR(123) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
+ PRIMARY KEY (`id`)
+) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+
+UPDATE `aowow_dbversion` SET `sql` = CONCAT(IFNULL(`sql`, ''), ' emotes');
diff --git a/setup/sql/updates/1692289951_01.sql b/setup/sql/updates/1692289951_01.sql
new file mode 100644
index 00000000..3528f2b5
--- /dev/null
+++ b/setup/sql/updates/1692289951_01.sql
@@ -0,0 +1,17 @@
+DROP TABLE IF EXISTS `aowow_declinedword`;
+DROP TABLE IF EXISTS `aowow_declinedwordcases`;
+
+CREATE TABLE `aowow_declinedword` (
+ `id` SMALLINT(5) UNSIGNED NOT NULL,
+ `word` VARCHAR(127) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
+ PRIMARY KEY (`id`)
+) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+
+CREATE TABLE `aowow_declinedwordcases` (
+ `wordId` SMALLINT(5) UNSIGNED NOT NULL,
+ `caseIdx` TINYINT(1) UNSIGNED NOT NULL,
+ `word` VARCHAR(131) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
+ PRIMARY KEY (`wordId`, `caseIdx`)
+) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+
+UPDATE `aowow_dbversion` SET `sql` = CONCAT(IFNULL(`sql`, ''), ' declinedwords');
diff --git a/setup/sql/updates/1693485224_01.sql b/setup/sql/updates/1693485224_01.sql
new file mode 100644
index 00000000..3db61fd4
--- /dev/null
+++ b/setup/sql/updates/1693485224_01.sql
@@ -0,0 +1,12 @@
+DROP TABLE IF EXISTS `aowow_screeneffect_sounds`;
+CREATE TABLE `aowow_screeneffect_sounds` (
+ `id` SMALLINT(5) unsigned NOT NULL,
+ `name` VARCHAR(40) COLLATE utf8mb4_unicode_ci NOT NULL,
+ `ambienceDay` SMALLINT(5) unsigned NOT NULL,
+ `ambienceNight` SMALLINT(5) unsigned NOT NULL,
+ `musicDay` SMALLINT(5) unsigned NOT NULL,
+ `musicNight` SMALLINT(5) unsigned NOT NULL,
+ KEY `id` (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+
+UPDATE `aowow_dbversion` SET `sql` = CONCAT(IFNULL(`sql`, ''), ' sounds');
diff --git a/setup/sql/updates/1702576294_01.sql b/setup/sql/updates/1702576294_01.sql
new file mode 100644
index 00000000..bef0e1a9
--- /dev/null
+++ b/setup/sql/updates/1702576294_01.sql
@@ -0,0 +1 @@
+UPDATE `aowow_dbversion` SET `sql` = CONCAT(IFNULL(`sql`, ''), ' source');
diff --git a/setup/sql/updates/1709133536_01.sql b/setup/sql/updates/1709133536_01.sql
new file mode 100644
index 00000000..4df33585
--- /dev/null
+++ b/setup/sql/updates/1709133536_01.sql
@@ -0,0 +1,14 @@
+DELETE FROM `aowow_setup_custom_data` WHERE `command` = 'classes' AND `field` = 'roles';
+INSERT INTO `aowow_setup_custom_data` VALUES
+ ('classes',1,'roles','10','Warrior - rngDPS'),
+ ('classes',2,'roles','11','Paladin - mleDPS + Tank + Heal'),
+ ('classes',3,'roles','4','Hunter - rngDPS'),
+ ('classes',4,'roles','2','Rogue - mleDPS'),
+ ('classes',5,'roles','5','Priest - rngDPS + Heal'),
+ ('classes',6,'roles','10','Death Knight - mleDPS + Tank'),
+ ('classes',7,'roles','7','Shaman - mleDPS + rngDPS + Heal'),
+ ('classes',8,'roles','4','Mage - rngDPS'),
+ ('classes',9,'roles','4','Warlock - rngDPS'),
+ ('classes',11,'roles','15','Druid - mleDPS + Tank + Heal + rngDPS');
+
+UPDATE `aowow_dbversion` SET `sql` = CONCAT(IFNULL(`sql`, ''), ' classes');
diff --git a/setup/sql/updates/1709143189_01.sql b/setup/sql/updates/1709143189_01.sql
new file mode 100644
index 00000000..bef0e1a9
--- /dev/null
+++ b/setup/sql/updates/1709143189_01.sql
@@ -0,0 +1 @@
+UPDATE `aowow_dbversion` SET `sql` = CONCAT(IFNULL(`sql`, ''), ' source');
diff --git a/setup/sql/updates/1709154964_01.sql b/setup/sql/updates/1709154964_01.sql
new file mode 100644
index 00000000..a75d1bfa
--- /dev/null
+++ b/setup/sql/updates/1709154964_01.sql
@@ -0,0 +1 @@
+UPDATE `aowow_dbversion` SET `sql` = CONCAT(IFNULL(`sql`, ''), ' zones');
diff --git a/setup/sql/updates/1709226658_01.sql b/setup/sql/updates/1709226658_01.sql
new file mode 100644
index 00000000..0ef56225
--- /dev/null
+++ b/setup/sql/updates/1709226658_01.sql
@@ -0,0 +1 @@
+UPDATE `aowow_dbversion` SET `build` = CONCAT(IFNULL(`build`, ''), ' power');
diff --git a/setup/sql/updates/1710542696_01.sql b/setup/sql/updates/1710542696_01.sql
new file mode 100644
index 00000000..d6f6f21d
--- /dev/null
+++ b/setup/sql/updates/1710542696_01.sql
@@ -0,0 +1 @@
+UPDATE `aowow_dbversion` SET `sql` = CONCAT(IFNULL(`sql`, ''), ' objects');
diff --git a/setup/sql/updates/1711739612_01.sql b/setup/sql/updates/1711739612_01.sql
new file mode 100644
index 00000000..1a319ead
--- /dev/null
+++ b/setup/sql/updates/1711739612_01.sql
@@ -0,0 +1,11 @@
+DELETE FROM aowow_articles WHERE `type` = 13 AND `locale` = 3 AND `typeId` IN (1,2,3,4,5,6,7,8,9,11);
+INSERT INTO aowow_articles (`type`, `typeId`, `locale`, `article`) VALUES (13, 1, 3, '[b][color=c1]Krieger[/color][/b] sind eine sehr mächtige Klasse, die sowohl tanken als auch im Nahkampf erheblichen Schaden anrichten kann. Der [icon name=ability_warrior_defensivestance][url=?spells=7.1.257]Schutz[/url][/icon]-Talentbaum des Kriegers enthält viele Talente, um seine Überlebensfähigkeit zu verbessern und Bedrohung gegenüber Monstern zu erzeugen. Schutz-Krieger sind eine der wichtigsten Tank-Klassen des Spiels.\n\nAußerdem verfügen Krieger über zwei schadensorientierte Talentbäume - [icon name=ability_rogue_eviscerate][url=?spells=7.1.26]Waffen[/url][/icon] und [icon name=ability_warrior_innerrage][url=?spells=7.1.256]Furor[/url][/icon]. Der Furor-Talentbaum enthält das Talent [spell=46917], das es dem Krieger erlaubt, zwei Zweihandwaffen gleichzeitig zu führen! Krieger sind in der Lage, mit Fähigkeiten wie [spell=845], [spell=1680] und [spell=46924] starken Flächenschaden im Nahkampf zu verursachen. Ein Krieger kämpft in einer bestimmten [i]Haltung[/i], die ihm Boni und Zugang zu verschiedenen Fähigkeiten gewährt. Zu Beginn verfügen Krieger nur über die [spell=2457], erlernen aber mit Level 10 [spell=71] und mit Level 30 [spell=2458]. Die Verteidigungshaltung wird zum Tanken, die Kampfhaltung oder Berserkerhaltung für erheblichen Nahkampfschaden verwendet.\n\n[ul][li]Alle Krieger können ihren Schlachtzug oder ihre Gruppe mit einem [spell=6673] oder [spell=469] verstärken. Furor-Krieger besitzen den passiven Stärkungszauber [spell=29801], der die Chance auf kritische Treffer im Nah- und Fernkampf für ihre Verbündeten deutlich erhöht.[/li][li]Krieger haben zahlreiche nützliche Fähigkeiten, um schnell an ihr Ziel zu gelangen! Alle Krieger können [spell=100] oder [spell=20252] benutzen, um einen Gegner zu erreichen. Zudem können sie schnell [spell=3411], um ein befreundetes Ziel vor einem Angriff zu schützen.[/li][/ul]');
+INSERT INTO aowow_articles (`type`, `typeId`, `locale`, `article`) VALUES (13, 2, 3, '[b][color=c2]Paladine[/color][/b] unterstützen ihre Verbündeten mit heiligen Auren und Segen, um sie vor Schaden zu bewahren und ihre Kräfte zu stärken. Sie tragen Plattenrüstungen und können in den härtesten Schlachten verheerenden Schlägen standhalten, während sie ihre Verwundeten heilen und die Gefallenen wiederbeleben. Im Kampf können sie mächtige Zweihandwaffen führen, ihre Feinde betäuben, Untote und Dämonen vernichten und ihre Feinde mit heiliger Vergeltung richten. Paladine sind eine defensive Klasse, die in erster Linie darauf ausgelegt ist, ihre Gegner zu überdauern.\n\nDer Paladin ist hauptsächlich ein Nahkämpfer und in geringem Maße Zauberer, der aufgrund seiner [url=?spells=7.2&filter=cr=109:12;crs=10:1;crv=0:0]Heilzauber[/url], [url=?spells=7.2&filter=na=Segen]Segen[/url] und anderen Fähigkeiten sehr nützlich für die Gruppe ist. Sie können eine aktive [url=?spells=7.2&filter=na=Aura]Aura[/url] pro Paladin auf alle Gruppen- und Schlachtzugsmitglieder legen und bestimmte Segen für bestimmte Spieler verwenden. Dank ihrer zahlreichen defensiven Fähigkeiten vergessen Paladine einfach unglaublich oft zu sterben. Mit ihrer Fähigkeit [spell=25780] sind sie außerdem ausgezeichnete Tanks.\n\n[ul][li]Paladine können effektiv [icon name=spell_holy_holybolt][url=?spells=7.2.594]heilen[/url][/icon], [icon name=spell_holy_devotionaura][url=?spells=7.2.267]tanken[/url][/icon] und im Nahkampf [icon name=spell_holy_auraoflight][url=?spells=7.2.184]Schaden[/url][/icon] verursachen.[/li][li]Sie besitzen eine große Auswahl an Segen, Auren und anderen Verstärkungszaubern.[/li][li]Der Paladin ist die einzige Klasse mit Zugang zu einem echten Unverwundbarkeitszauber: [spell=642].[/li][/ul]');
+INSERT INTO aowow_articles (`type`, `typeId`, `locale`, `article`) VALUES (13, 3, 3, '[b][color=c3]Jäger[/color][/b] sind eine besonders einzigartige Klasse in World of Warcraft. Sie sind die einzigen nicht-magischen Fernkämpfer, die mit Bögen und Gewehren kämpfen. Jäger verfügen über verschiedene Arten von Schüssen und Bissen zur Schwächung ihrer Gegner und können [url=?spells=7.3&filter=cr=4;crs=1;crv=0;na=Falle]Fallen[/url] legen, um Schaden zu verursachen oder den Gegner auf andere Weise zu verlangsamen oder kampfunfähig zu machen.\n\nJäger [icon name=ability_hunter_beasttaming][url=?spell=1515]zähmen Wildtiere[/url][/icon], damit diese sie als [url=?pets]Begleiter[/url] im Kampf unterstützen. Zwar sind Jäger nicht die einzige Klasse, die Begleiter einsetzen kann. Ihre Tierbegleiter sind aber insofern einzigartig, als jede Spezies einen [url=?petcalc]eigenen Talentbaum[/url] hat, den der Jäger nutzen kann, um Punkte auf verschiedene Fähigkeiten zu verteilen.\n\nDarüber hinaus hat jede Spezies eine einzigartige Spezialfähigkeit. Jäger können sich die begehrtesten Begleiter aufgrund ihres Aussehens oder ihrer Fähigkeiten aussuchen. Und wenn sie genug Talentpunkte in den Baum der [icon name=ability_hunter_beasttaming][url=?spells=7.3.50]Tierherrschaft[/url][/icon] investieren, können sie besondere, "exotische" Bestien zähmen, wie [url=?pet=46]Geisterbestien[/url] oder [url=?pet=39]Teufelssaurier[/url]!\n\n[ul][li]Jäger haben Zugriff auf 25 (32 als [icon name=ability_hunter_beastmastery][url=?spell=53270]Meister der Tierherrschaft[/url][/icon]) verschiedene Arten von Begleitern mit über 150 verschiedenen Erscheinungsbildern![/li][li]Jäger haben eine Reihe von überlebensorientierten Fähigkeiten, die sie einsetzen können, um potentiellen Gefahren zu entkommen oder ihnen auszuweichen, wie z.B. [spell=5384] und [spell=781].[/li][li]Auf das [icon name=ability_hunter_swiftstrike][url=?spells=7.3.51]Überleben[/url][/icon] spezialisierte Jäger können in ihrem Talentbaum Punkte in das Talent [icon name=ability_hunter_huntingparty][url=?spells=-2.3&filter=na=jagdgesellschaft rel=spell=53292]Jagdgesellschaft[/url][/icon] investieren, welches es ihnen ermöglicht, ihre Gruppen- und Schlachtzugsmitglieder mit dem Stärkungszauber [spell=57669] zu versorgen.[/li][/ul]');
+INSERT INTO aowow_articles (`type`, `typeId`, `locale`, `article`) VALUES (13, 4, 3, '[b][color=c4]Schurken[/color][/b] sind eine Nahkampfklasse, die Lederrüstungen trägt und ihren Feinden mit sehr schnellen Angriffen großen Schaden zufügen kann. Sie sind Meister der Verstohlenheit und des Meuchelns, die sich ungesehen an Feinden vorbeischleichen, aus den Schatten heraus zuschlagen und dann blitzschnell aus dem Kampf verschwinden.\n\nSie sind in der Lage, [url=?items=0.-3&filter=cr=152;crs=4;crv=0;ty=-3#0+1-2]Gifte[/url] einzusetzen, um ihre Gegner zu verkrüppeln und sie so im Kampf massiv zu schwächen. Schurken verfügen über ein mächtiges Arsenal an Fähigkeiten, von denen viele dadurch verstärkt werden, dass sie in [spell=1784] schleichen und ihre Opfer kampfunfähig machen können.\n\nSchurken können sich auf drei unterschiedliche Kampfstile mithilfe ihrer Talentbäume Meucheln, Kampf und Täuschung spezialisieren.\n\nAuf das [icon name=ability_rogue_eviscerate][url=?spells=7.4.253]Meucheln[/url][/icon] spezialisierte Schurken sind [icon name=ability_creature_poison_06][url=?spells=-2&filter=na=meister+der+gifte rel=spell=58410]Meister der Gifte[/url][/icon] und [icon name=ability_rogue_disembowel][url=?spell=57993]vergiften[/url][/icon] ihre Gegner mit schnellen Dolchen, die mit [icon name=ability_rogue_feigndeath][url=?spells=-2.4.253&filter=na=Üble+Gifte rel=spell=16515]üblen[/url][/icon] und [icon name=ability_poisons][url=?spells=-2.4.253&filter=na=Verbesserte+Gifte rel=spell=14117]verbesserten[/url][/icon] Giften versehen sind.\n\nAuf den [icon name=ability_backstab][url=?spells=7.4.38]Kampf[/url][/icon] spezialisierte Schurken können den Umgang mit [icon name=inv_sword_27][url=?spells=-2&filter=na=Niedermetzeln rel=spell=13964]Axt und Schwert[/url][/icon] oder [icon name=inv_mace_01][url=?spells=-2&filter=na=Streitkolben-Spezialisierung;cl=4 rel=spell=13803]Streitkolben[/url][/icon] meistern und haben mithilfe ihrer Talente auch in langwierigen Kämpfen eine verbesserte Energiezufuhr, um zuverlässig ihre Angriffscombos durchzuführen.\n\nAuf die [icon name=ability_stealth][url=?spells=7.4.39]Täuschung[/url][/icon] spezialisierte Schurken besitzen Fähigkeiten, die unvorhergesehene Aktionen ermöglichen. So können sie dank [spell=51713] etwa kurzzeitig Fähigkeiten nutzen, die eigentlich nur aus der Verstohlenheit heraus nutzbar wären, mit [spell=36554] plötzlich hinter einem Gegner auftauchen oder mit [icon name=ability_rogue_cheatdeath][url=?spells=-2.4.39&filter=cr=15;crs=0;crv=ability_rogue_cheatdeath rel=spell=31230]Von der Schippe springen[/url][/icon] einen sicheren Todesstoß überleben.');
+INSERT INTO aowow_articles (`type`, `typeId`, `locale`, `article`) VALUES (13, 5, 3, '[b][color=c5]Priester[/color][/b] gelten allgemein als eine der Standard-Heilerklassen in World of Warcraft, da sie über zwei Talentspezialisierungen zur effektiven Heilung verfügen.\n\nIhr [icon name=spell_holy_holybolt][url=?spells=7.5.56]Heilig[/url][/icon]-Talentbaum enthält Talente, die die Heilung auf ihre Verbündeten erheblich verstärken - einschließlich Zaubern, mit denen mehrere Spieler gleichzeitig geheilt werden können, wie z.B. [spell=48089].\nDer [icon name=spell_holy_wordfortitude][url=?spells=7.5.613]Disziplin[/url][/icon]-Talentbaum ist zwar auch in der Lage, eine beträchtliche Menge an Heilung zu bewirken, konzentriert sich aber in erster Linie auf die Schadensabsorption und -verminderung durch den Einsatz von [spell=48066] und [icon name=spell_holy_devineaegis][url=?spells=-2.5&filter=cr=15;crs=0;crv=spell_holy_devineaegis rel=spell=47515]Göttliche Aegis[/url][/icon].\nPriester können außerdem mit ihren [icon name=spell_shadow_shadowwordpain][url=?spells=7.5.78]Schatten[/url][/icon]fähigkeiten sehr mächtigen Fernkampfschaden verursachen. Insbesondere wenn sie ihre [spell=15473] annehmen, erhöht sich ihr Schattenschaden erheblich, aber sie verlieren ihre Fähigkeit, Heiligzauber zu wirken.\n\n[ul][li]Der Disziplin-Talentbaum wird in der Regel zur Heilung verwendet, enthält aber auch einige Talente zur Erhöhung des Schadens des Priesters, wobei Schattenzauber und -fähigkeiten in erster Linie zur Verursachung von Fernkampfschaden verwendet werden sollten.[/li][li]Priester verfügen über einen der am meisten geschätzten Stärkungszauber im Spiel - [spell=48161], welcher allen befreundeten Mitspielern eine unverzichtbare Erhöhung ihrer Ausdauer gewährt. Außerdem können sie ihre Mitspieler mit [spell=48073] und [spell=48169] verstärken und mit einzigartigen Hymnen das [icon name=spell_holy_divinehymn][url=?spell=64843]Leben[/url][/icon] und [icon name=spell_holy_symbolofhope][url=?spell=64901]Mana[/url][/icon] ihres Schlachtzug signifikant wiederherstellen![/li][li]Schattenpriester unterstützen, zusätzlich zu ihrem Schaden, jeden Schlachtzug mit dem beliebten Stärkungszauber [spell=57669] zur Erhöhung der Manaregeneration und mit ihrer [spell=15286], die ihre gesamte Gruppe passiv heilt.[/li][/ul]');
+INSERT INTO aowow_articles (`type`, `typeId`, `locale`, `article`) VALUES (13, 6, 3, 'Die [b][color=c6]Todesritter[/color][/b] wurden in der Erweiterung Wrath of the Lich King eingeführt und sind die erste Heldenklasse von World of Warcraft. Todesritter beginnen auf Stufe 55 in einer speziellen, instanzierten Zone, die für andere Klassen unzugänglich ist: [url=?maps=4298:511346]Acherus, die Schwarze Festung[/url] in der Scharlachroten Enklave der Östlichen Pestländer. Hier erhalten sie ihre Talentpunkte durch Questbelohnungen und bekommen sogar ein besonderes beschworenes Reittier: das [spell=48778]!\n\nTodesritter haben mehrere sehr starke Möglichkeiten zur Schadensverursachung, da jeder ihrer Talentbäume es erlaubt mit einer Vielfalt an Nahkampffähigkeiten, Zaubern und Schaden-über-Zeit verursachenden Krankheiten überragende Leistung zu erbringen. Sie sind auch sehr fähige Tanks, wobei sowohl ihr Blut- als auch ihr Frost-Talentbaum einzigartige Optionen bietet. [icon name=spell_deathknight_bloodpresence][url=?spells=7.6.770]Blut[/url][/icon] bietet mehr Selbstheilungsfähigkeiten, [icon name=spell_deathknight_frostpresence][url=?spells=7.6.771]Frost[/url][/icon] bietet erhebliche Schadensminderung und starken Flächenschaden.\n\nTodesritter kämpfen mit einem besonderen Verstärkungszauber, der [url=?spells=7&filter=na=präsenz]Präsenz[/url] genannt wird (ähnlich wie die Haltungen eines Kriegers), der ihnen besondere Boni für ihre Rollen verleiht. Todesritter verwenden ein einzigartiges Ressourcensystem, bei dem die meisten Zauber entweder [url=?spells=7.6&filter=cr=45;crs=10;crv=0#50+1+13+3]Runen[/url] kosten, die während des Kampfes wieder aufgefüllt werden, oder [url=?spells=7.6&filter=cr=45;crs=11;crv=0]Runenmacht[/url], die durch verschiedene Fähigkeiten erzeugt werden kann.\n\n[ul][li]Auf [icon name=spell_deathknight_unholypresence][url=?spells=7.6.772]Unheilig[/url][/icon] spezialisierte Todesritter können sich in [spell=52143] spezialisieren, was ihren beschworenen Ghul-Wächter zu einem permanenten Begleiter macht, der sie im Kampf unterstützt![/li][li]Die Klasse der Todesritter verfügt über eine eigene spezielle Waffenverzauberungsfähigkeit namens [spell=53428], die herkömmliche Waffenverzauberungen überflüssig macht.[/li][li]Todesritter sind eine Schadensklasse, die ihren Schaden sowohl durch Nahkampffähigkeiten als auch durch Zauber verursacht![/li][/ul]');
+INSERT INTO aowow_articles (`type`, `typeId`, `locale`, `article`) VALUES (13, 7, 3, '[b][color=c7]Schamanen[/color][/b] beherrschen Elementar- und Naturmagie und bringen einer (Schlachtzugs-) Gruppe die größte Vielfalt an potenziellen Stärkungszaubern in Form von [url=?spells=7&filter=na=Totem;cl=7]Totems[/url]. Ein Schamane kann für jedes Element - Erde, Feuer, Luft und Wasser - ein Totem beschwören, welches zu seinen Füßen erscheint und allen Mitgliedern seiner (Schlachtzugs-) Gruppe in Reichweite einen Stärkungszauber verleiht. Einige Totems, insbesondere Feuer-Totems, fügen Gegnern auch Schaden zu. Der Trick beim Spielen jeder Art von Schamanen besteht darin, zu wissen, welche Totems in welcher Situation beschworen werden müssen, um den verursachten Schaden und die Überlebensfähigkeit ihrer Gruppe zu maximieren.\n\nSchamanen sind in erster Linie Zauberer, wobei ein auf [icon name=spell_nature_lightningshield][url=?spells=7.7.373]Verstärkung[/url][/icon] spezialisierter Schamane Schaden in Nahkampfreichweite verursacht. Ein solcher Schamane erlernt das Führen zweier Waffen durch [spell=30798] und kann mit [spell=51533] zwei Schattenwölfe zur Unterstützung im Kampf beschwören. Obwohl sie hauptsächlich im Nahkampf eingesetzt werden, können auf Verstärkung spezialisierte Schamanen dennoch einen gewissen Nutzen aus ihrer Zaubermacht ziehen und spontane [icon name=spell_nature_lightning][url=?spell=49238]Blitzschläge[/url][/icon] oder [url=?spells=7&filter=cr=109:12:14;crs=10:1:5;crv=0:0:60000;cl=7]Heilungen[/url] durch [icon name=spell_shaman_maelstromweapon][url=?spells=-2&filter=na=waffe+des+mahlstroms rel=spell=51532]Waffe des Mahlstroms[/url][/icon] wirken.\n\nAuf [icon name=spell_nature_lightning][url=?spells=7.7.375]Elementarkampf[/url][/icon] spezialisierte Schamanen wirken Feuer- und Blitzzauber auf Distanz und verursachen so großen Schaden. Sie können Gegner durch [spell=59159] zurückstoßen und mit [icon name=spell_shaman_stormearthfire][url=?spells=-2&filter=na=sturm%2C+erde+und+feuer rel=spell=51486]Sturm, Erde und Feuer[/url][/icon] alle Feinde in einem Gebiet festwurzeln. Außerdem gewähren sie durch [spell=57722] und [icon name=spell_shaman_elementaloath][url=?spells=-2&filter=na=Elementarer+Schwur rel=spell=51470]Elementarer Schwur[/url][/icon] begehrte Stärkungszauber für Zauberer ihres Schlachtzugs.\n\nEin auf [icon name=spell_nature_magicimmunity][url=?spells=7.7.374]Wiederherstellung[/url][/icon] spezialisierter Schamane erhält verbesserte Heilzauber und kann ein ausgezeichneter Schlachtzugs- oder Tankheiler sein. Sie sind bekannt für ihre mächtige Fähigkeit [spell=55459] und dafür, dass sie ein [spell=16190] zur Verfügung stellen, welches der Gruppe hilft Mana wiederherzustellen. Sie erhalten außerdem ein mächtiges [spell=49284], können mit [spell=51886] Flüche entfernen und verfügen durch [spell=61301] über einen Spontanheilungseffekt, der zusätzlich eine Heilung über Zeit verursacht.\n\n[ul][li]Es gibt über zwanzig verschiedene Totems, die ein Schamane erlernen kann![/li][li]Schamanen der Horde können [spell=2825] und Schamanen der Allianz [spell=32182] wirken, wodurch der verursachte Schaden und die gewirkte Heilung der gesamten Gruppe erhöht wird. Dieser Stärkungszauber ist einzigartig und in jeder Schlachtzugsgruppe sehr begehrt.[/li][li]Ein Schamane kann sich ab Stufe 16 in einen [spell=2645] verwandeln und dies mit dem Talent [spell=16287] sogar als Spontanzauber wirken. Dieser Zauber kann im Kampf eingesetzt werden, aber nicht in geschlossenen Räumen.[/li][li]Schamanen können immer nur einen Elementarschild - [spell=49281] oder [spell=57960] - gleichzeitig benutzen. Auf Wiederherstellung spezialisierte Schamanen können zudem [spell=49284] auf einen anderen Spieler wirken.[/li][/ul]');
+INSERT INTO aowow_articles (`type`, `typeId`, `locale`, `article`) VALUES (13, 8, 3, '[b][color=c8]Magier[/color][/b] bändigen die Elemente Feuer, Frost und Arkan, um ihre Feinde zu vernichten oder unter Kontrolle zu halten. Dazu besitzen sie ein Arsenal voller Zauber zu unterschiedlichen Zwecken.\nStärkungszauber, [icon name=ability_mage_conjurefoodrank10][url=?spell=42956]herbeigezauberte Erfrischungen[/url][/icon] oder arkane [url=?spells=7&filter=na=Portal]Portale[/url] zur schnellen Weltreise in ferne Länder machen einen Magier zu einem idealen Weggefährten.\nUnd wenn man eine Klasse sucht, die Gegner in eine Welt des Schmerzes einführt, ist der Magier eine gute Wahl. Ihren Gegnern können Magier mit verschiedensten Schwächungszaubern die Bedingungen eines jeden Kampfes diktieren, mit Elementarblitzen massiven Schaden aus der Ferne anrichten, oder Zerstörung in einem großen Wirkungsbereich niederregnen lassen.\n\nAuf [icon name=spell_holy_magicalsentry][url=?spells=7.8.237]Arkan[/url][/icon] spezialisierte Magier haben das Potenzial, mit [icon name=spell_arcane_blast][url=?spell=42897]Arkanschlägen[/url][/icon] und [icon name=ability_mage_missilebarrage][url=?spells=-2&filter=na=Geschosssalve rel=spell=54490]Salven[/url][/icon] an [icon name=spell_nature_starfall][url=?spell=42846]Arkanen Geschossen[/url][/icon] in kurzer Zeit enormen Schaden zu verursachen. Das Bändigen der reinen arkanen Mächte hat jedoch ihre Kehrseite: einem unerfahrenen Arkanmagier verzehrt es schon nach kurzer Zeit seine gesamten Kräfte.\n\nAuf [icon name=spell_fire_flamebolt][url=?spells=7.8.8]Feuer[/url][/icon] spezialisierte Magier verfallen durch kritische Treffer mit Feuerzaubern in [icon name=ability_mage_hotstreak][url=?spells=-2&filter=na=Kampfeshitze rel=spell=44448]Kampfeshitze[/url][/icon] und äschern so ihre Gegner mit verheerenden [icon name=spell_fire_fireball02][url=?spell=42891]Pyroschlägen[/url][/icon] ein. Zudem verwandeln sie ihre Gegner in [icon name=ability_mage_livingbomb][url=?spell=55360]Lebende Bomben[/url][/icon] und verursachen dadurch explosiven Flächenschaden.\n\n[icon name=spell_frost_frostbolt02][url=?spells=7.8.6]Frost[/url][/icon]magier können ihre Gegner [icon name=ability_mage_deepfreeze][url=?spell=44572]in Eis erstarren[/url][/icon] lassen. Ihre Spezialisierung auf Kälteeffekte erlaubt ihnen eine starke Kontrolle über ihre Gegner und erhöht dadurch ihre Überlebensfähigkeit enorm.\n\n[ul][li]Magier können Erfrischungen herbeizaubern, um die Gesundheit und das Mana ihrer Verbündeten wiederherzustellen.[/li][li]Sie sind die einzige Klasse, die Portale erschaffen kann, um andere Spieler zu transportieren. Sie können jedoch keine Spieler von einem entfernten Ort herbeirufen - das ist die Aufgabe eines [class=9]![/li][li]Der verursachte Fernkampfschaden von Magiern ist einer der höchsten im Spiel und macht sie zu einem unverzichtbaren Verbündeten in jedem Schlachtzug.[/li][/ul]');
+INSERT INTO aowow_articles (`type`, `typeId`, `locale`, `article`) VALUES (13, 9, 3, '[b][color=c9]Hexenmeister[/color][/b] sind Meister der dämonischen Künste. Gekleidet in Gewänder sind sie Meister im Wirken von [url=?spells=7&filter=cr=12;crs=1;crv=0;na=Fluch+der;cl=9]Flüchen[/url], dem Schleudern von Feuer- oder Schattenblitzen und der Beschwörung von [url=?spells=7&filter=cr=14;crs=6;crv=48018;na=beschwören;cl=9]Dämonen[/url] unter ihre Kontrolle zur Unterstützung im Kampf. Die Kombination ihrer Flüche und direkten Schadenszauber richten Verwüstung und Zerstörung an und machen Hexenmeister zu sehr gefürchteten Gegnern.\n\nNeben Mana als primäre Ressource können Hexenmeister Gegnern Teile ihrer [icon name=spell_shadow_haunting][url=?spell=47855]Seele stehlen[/url][/icon] und dadurch [item=6265] erzeugen. Seelensplitter ermöglichen mächtige rituelle Magie, etwa zur [icon name=spell_shadow_twilight][url=?spell=698]Beschwörung von anderen Spielern[/url][/icon] oder von [icon name=spell_shadow_shadesofdarkness][url=?spell=58887]Gesundheitssteinen[/url][/icon] mit heilenden Kräften. Insbesondere kann jedoch ein Hexenmeister mit ihnen die Seele eines Verbündeten in einem [icon name=spell_shadow_soulgem][url=?spell=47884]Seelenstein[/url][/icon] speichern, sodass dieser im Todesfall sich selbst wiederbeleben kann.\n\n[ul][li]Hexenmeister können durch ein Ritual der Beschwörung ein Portal erschaffen, um einen anderen Spieler an den Ort des Portals zu beschwören.[/li][li]Sie können Gesundheitssteine beschwören, die den Anwender heilen.[/li][li]Die Flüche eines Hexenmeisters können ihre Feinde schwächen oder ihnen Schaden zufügen.[/li][/ul]');
+INSERT INTO aowow_articles (`type`, `typeId`, `locale`, `article`) VALUES (13, 11, 3, '[b][color=c11]Druiden[/color][/b] sind die "Alleskönner"-Klasse in World of Warcraft - das heißt, sie können in einer Vielzahl von verschiedenen Rollen agieren und bieten daher einen der vielfältigsten Spielstile. Durch das [i]Annehmen der Gestalt von verschiedenen Kreaturen[/i] kann der Druide heilen, Schaden im Nah- und Fernkampf verursachen oder als Tank agieren. Mit steigenden Stufen kann der Druide neue, immer mächtigere Gestaltwandlungen erlernen, um sich in eine Kreatur passend zu seiner Rolle zu verwandeln.\n\nAuf niedrigeren Stufen wird ein Druide in seiner humanoiden Gestalt heilen oder im Fernkampf Schaden verursachen. Auf späteren Stufen jedoch erhalten Druiden durch die spezialisierten Talentbäume Zugang zu zwei besonderen Gestalten für jede unterschiedliche Rolle.\n\nAuf [icon name=spell_nature_healingtouch][url=?spells=7.11.573]Wiederherstellung[/url][/icon] spezialisierte Druiden erlernen den [spell=33891], der die Manakosten ihrer Heilzauber reduziert und jegliche Heilung auf ihre Verbündeten verstärkt.\nAuf [icon name=spell_nature_starfall][url=?spells=7.11.574]Gleichgewicht[/url][/icon] spezialisierte Druiden verursachen Schaden im Fernkampf und erlernen die [spell=24858], die ihre Rüstung sowie die Chance auf kritische Treffer mit Zaubern bei ihnen und ihren Verbündeten erhöht.\nEs gibt auch zwei Druidenformen für den [icon name=ability_racial_bearform][url=?spells=7.11.134]Wilden Kampf[/url][/icon]. Zum einen die mächtige [spell=5487] (und [spell=9634] ab einer höheren Stufe) - eine auf das Tanken ausgelegte Gestalt, die zusätzliche Rüstung, Gesundheit und Zugang zu einem Arsenal von Fähigkeiten zur Erhöhung der Bedrohung und Schadensverminderung gewährt. Zum anderen die schurkenähnliche [spell=768], die erheblichen Nahkampfschaden verursachen kann.\n\n[ul][li]Druiden erlernen ihre verschiedenen Gestalten durch das Abschließen von Quests oder durch Training. Einige Gestalten können nur durch Talente erlernt werden.[/li][li]Es gibt einige Gestalten, die alle Druiden erlernen können. Die Bärengestalt erhält man ab Stufe 10, die [spell=1066] und [spell=783] ab Stufe 16, die Katzengestalt ab Stufe 20 und die Terrorbärengestalt ab Stufe 40.[/li][li]Druiden haben sogar ihre eigene fliegende Reisegestalt: die [spell=33943] kann ab Stufe 60 und die [spell=40120] ab Stufe 71 erlernt werden, sofern der Spieler bereits [icon name=spell_nature_swiftness][url=?spell=34093]Gekonntes Reiten[/url][/icon] erlernt hat.[/li][li]Einige Druidengestalten können nur über Talente erlernt werden - die Mondkingestalt kann ab Stufe 40 erlernt werden, wenn ein Spieler viele Talentpunkte im Gleichgewicht-Talentbaum verteilt, und Baum des Lebens ab Stufe 50, wenn er viele Talentpunkte im Wiederherstellung-Talentbaum verteilt.[/li][li]Druiden haben ihre eigene, klassenspezifische [icon name=spell_arcane_teleportmoonglade][url=?spell=18960]Teleportationsfähigkeit[/url][/icon], die es ihnen erlaubt, zur [zone=493] zu reisen - praktisch, wenn sie trainieren müssen![/li][li]Druiden in (Terror-) Bärengestalt oder Katzengestalt schwingen zur Verursachung von Nahkampfschaden keine Waffen. Stattdessen erhalten sie einen speziellen Wert für jede ausgerüstete Nahkampfwaffe: die "Angriffskraft in Tiergestalt". Dieser Wert ist eine Umwandlung des "Schaden pro Sekunde"-Wertes einer Waffe in einen Wert, der Angriffskraft verleiht und den verursachten Schaden des Druiden in Katzen- oder (Terror-) Bärengestalt beeinflusst.[/li][/ul]');
diff --git a/setup/sql/updates/1713730806_01.sql b/setup/sql/updates/1713730806_01.sql
new file mode 100644
index 00000000..00def1c1
--- /dev/null
+++ b/setup/sql/updates/1713730806_01.sql
@@ -0,0 +1,24 @@
+UPDATE aowow_dbversion SET `sql` = REPLACE(`sql`, 'searchplugin', '');
+UPDATE aowow_dbversion SET `sql` = REPLACE(`sql`, 'power', '');
+UPDATE aowow_dbversion SET `sql` = REPLACE(`sql`, 'searchboxScript', '');
+UPDATE aowow_dbversion SET `sql` = REPLACE(`sql`, 'demo', '');
+UPDATE aowow_dbversion SET `sql` = REPLACE(`sql`, 'searchboxBody', '');
+UPDATE aowow_dbversion SET `sql` = REPLACE(`sql`, 'realmMenu', '');
+UPDATE aowow_dbversion SET `sql` = REPLACE(`sql`, 'locales', '');
+UPDATE aowow_dbversion SET `sql` = REPLACE(`sql`, 'markup', '');
+UPDATE aowow_dbversion SET `sql` = REPLACE(`sql`, 'itemScaling', '');
+UPDATE aowow_dbversion SET `sql` = REPLACE(`sql`, 'realms', '');
+UPDATE aowow_dbversion SET `sql` = REPLACE(`sql`, 'statistics', '');
+UPDATE aowow_dbversion SET `sql` = REPLACE(`sql`, 'simpleImg', '');
+UPDATE aowow_dbversion SET `sql` = REPLACE(`sql`, 'complexImg', '');
+UPDATE aowow_dbversion SET `sql` = REPLACE(`sql`, 'talentCalc', '');
+UPDATE aowow_dbversion SET `sql` = REPLACE(`sql`, 'pets', '');
+UPDATE aowow_dbversion SET `sql` = REPLACE(`sql`, 'talentIcons', '');
+UPDATE aowow_dbversion SET `sql` = REPLACE(`sql`, 'glyphs', '');
+UPDATE aowow_dbversion SET `sql` = REPLACE(`sql`, 'itemsets', '');
+UPDATE aowow_dbversion SET `sql` = REPLACE(`sql`, 'enchants', '');
+UPDATE aowow_dbversion SET `sql` = REPLACE(`sql`, 'gems', '');
+UPDATE aowow_dbversion SET `sql` = REPLACE(`sql`, 'profiler', '');
+UPDATE aowow_dbversion SET `sql` = REPLACE(`sql`, 'weightPresets', '');
+UPDATE aowow_dbversion SET `sql` = REPLACE(`sql`, 'soundfiles', '');
+
diff --git a/setup/sql/updates/1716305876_01.sql b/setup/sql/updates/1716305876_01.sql
new file mode 100644
index 00000000..434c71b5
--- /dev/null
+++ b/setup/sql/updates/1716305876_01.sql
@@ -0,0 +1,7 @@
+SET foreign_key_checks = 0;
+
+ALTER TABLE `aowow_account_weightscales`
+ DROP PRIMARY KEY,
+ ADD PRIMARY KEY (`id`);
+
+SET foreign_key_checks = 1;
diff --git a/setup/sql/updates/1716918678_01.sql b/setup/sql/updates/1716918678_01.sql
new file mode 100644
index 00000000..8ffa2635
--- /dev/null
+++ b/setup/sql/updates/1716918678_01.sql
@@ -0,0 +1,44 @@
+-- undo sunken temple data
+DELETE FROM aowow_setup_custom_data WHERE `command` = 'zones' AND `entry` IN (1477, 1417);
+-- undo icc unused subzone linking (still has EXCLUDE_FOR_LISTVIEW set)
+DELETE FROM aowow_setup_custom_data WHERE `command` = 'zones' AND `field` = 'parentAreaId' AND `value` = 4812;
+-- undo Hellfire Citadel recategorization
+DELETE FROM aowow_setup_custom_data WHERE `command` = 'quests' AND `field` = 'zoneOrSort' AND `entry` IN (9572, 9575, 11354, 9589, 9590, 9607, 9608, 11362, 9492, 9493, 9494, 9495, 9496, 9497, 9524, 9525, 11363, 11364);
+INSERT INTO aowow_setup_custom_data VALUES
+ ('zones', 1417, 'cuFlags', 1073741824, 'Sunken Temple [extra area on map 109] - set: CUSTOM_EXCLUDE_FOR_LISTVIEW'),
+ ('zones', 22, 'cuFlags', 1073741824, 'Programmer Isle - set: CUSTOM_EXCLUDE_FOR_LISTVIEW'),
+ ('zones', 151, 'cuFlags', 1073741824, 'Designer Island - set: CUSTOM_EXCLUDE_FOR_LISTVIEW'),
+ ('zones', 3948, 'cuFlags', 1073741824, 'Brian and Pat Test - set: CUSTOM_EXCLUDE_FOR_LISTVIEW'),
+ ('zones', 4019, 'cuFlags', 1073741824, 'Development Land - set: CUSTOM_EXCLUDE_FOR_LISTVIEW'),
+ ('zones', 3605, 'cuFlags', 1073741824, 'Hyjal Past [extra area on map 560] - set: CUSTOM_EXCLUDE_FOR_LISTVIEW'),
+ ('zones', 3535, 'cuFlags', 1073741824, 'Hellfire Citadel [extra area on map 540] - set: CUSTOM_EXCLUDE_FOR_LISTVIEW'),
+ -- move quests from generic Hellfire Citadel to...
+ -- ...Hellfire Ramparts [3562]
+ ('quests', 9572, 'zoneOrSort', 3562, 'Weaken the Ramparts - category Hellfire Citadel -> Hellfire Ramparts'),
+ ('quests', 9575, 'zoneOrSort', 3562, 'Weaken the Ramparts - category Hellfire Citadel -> Hellfire Ramparts'),
+ ('quests', 11354, 'zoneOrSort', 3562, "Wanted: Nazan's Riding Crop - category Hellfire Citadel -> Hellfire Ramparts"),
+ -- ...The Blood Furnace [3713]
+ ('quests', 9589, 'zoneOrSort', 3713, 'The Blood is Life - category Hellfire Citadel -> Blood Furnace'),
+ ('quests', 9590, 'zoneOrSort', 3713, 'The Blood is Life - category Hellfire Citadel -> Blood Furnace'),
+ ('quests', 9607, 'zoneOrSort', 3713, 'Heart of Rage - category Hellfire Citadel -> Blood Furnace'),
+ ('quests', 9608, 'zoneOrSort', 3713, 'Heart of Rage - category Hellfire Citadel -> Blood Furnace'),
+ ('quests', 11362, 'zoneOrSort', 3713, "Wanted: Keli'dan's Feathered Stave - category Hellfire Citadel -> Blood Furnace"),
+ -- ...The Shattered Halls [3714]
+ ('quests', 9492, 'zoneOrSort', 3714, 'Turning the Tide - category Hellfire Citadel -> Shattered Halls'),
+ ('quests', 9493, 'zoneOrSort', 3714, 'Pride of the Fel Horde - category Hellfire Citadel -> Shattered Halls'),
+ ('quests', 9494, 'zoneOrSort', 3714, 'Fel Embers - category Hellfire Citadel -> Shattered Halls'),
+ ('quests', 9495, 'zoneOrSort', 3714, 'The Will of the Warchief - category Hellfire Citadel -> Shattered Halls'),
+ ('quests', 9496, 'zoneOrSort', 3714, 'Pride of the Fel Horde - category Hellfire Citadel -> Shattered Halls'),
+ ('quests', 9497, 'zoneOrSort', 3714, 'Emblem of the Fel Horde - category Hellfire Citadel -> Shattered Halls'),
+ ('quests', 9524, 'zoneOrSort', 3714, 'Imprisoned in the Citadel - category Hellfire Citadel -> Shattered Halls'),
+ ('quests', 9525, 'zoneOrSort', 3714, 'Imprisoned in the Citadel - category Hellfire Citadel -> Shattered Halls'),
+ ('quests', 11363, 'zoneOrSort', 3714, "Wanted: Bladefist's Seal - category Hellfire Citadel -> Shattered Halls"),
+ ('quests', 11364, 'zoneOrSort', 3714, 'Wanted: Shattered Hand Centurions - category Hellfire Citadel -> Shattered Halls');
+
+-- implement SpawnedByDefault
+ALTER TABLE aowow_spawns
+ MODIFY COLUMN `respawn` int signed NOT NULL DEFAULT 0;
+
+-- rebuild spawns
+UPDATE aowow_dbversion
+ SET `sql` = CONCAT(IFNULL(`sql`, ''), ' spawns quests');
diff --git a/setup/sql/updates/1717076299_01.sql b/setup/sql/updates/1717076299_01.sql
new file mode 100644
index 00000000..b272f9de
--- /dev/null
+++ b/setup/sql/updates/1717076299_01.sql
@@ -0,0 +1,72 @@
+ALTER TABLE aowow_config
+ MODIFY COLUMN `cat` tinyint unsigned NOT NULL DEFAULT 0,
+ MODIFY COLUMN `flags` smallint unsigned NOT NULL DEFAULT 0,
+ ADD COLUMN `default` varchar(255) DEFAULT NULL AFTER `value`;
+
+INSERT IGNORE INTO aowow_config VALUES
+ ('rep_req_border_unco', 5000, 5000, 5, 129, 'required reputation for uncommon quality avatar border'),
+ ('rep_req_border_rare', 10000, 10000, 5, 129, 'required reputation for rare quality avatar border'),
+ ('rep_req_border_epic', 15000, 15000, 5, 129, 'required reputation for epic quality avatar border'),
+ ('rep_req_border_lege', 25000, 25000, 5, 129, 'required reputation for legendary quality avatar border');
+
+UPDATE aowow_config SET `default` = 'UTF-8' WHERE `key` = 'default_charset';
+UPDATE aowow_config SET `comment` = 'website title' WHERE `key` = 'name';
+UPDATE aowow_config SET `comment` = 'feed title' WHERE `key` = 'name_short';
+UPDATE aowow_config SET `comment` = 'another halfbaked javascript thing..' WHERE `key` = 'board_url';
+UPDATE aowow_config SET `comment` = 'displayed sender for auth-mails, ect' WHERE `key` = 'contact_email';
+UPDATE aowow_config SET `comment` = 'pretend, we belong to a battlegroup to satisfy profiler-related javascripts' WHERE `key` = 'battlegroup';
+UPDATE aowow_config SET `comment` = 'points js to executable files', `flags` = `flags` | 768 WHERE `key` = 'site_host';
+UPDATE aowow_config SET `comment` = 'points js to images & scripts', `flags` = `flags` | 768 WHERE `key` = 'static_host';
+UPDATE aowow_config SET `comment` = 'some derelict code, probably unused' WHERE `key` = 'serialize_precision';
+UPDATE aowow_config SET `comment` = 'enter your GA-user here to track site stats' WHERE `key` = 'analytics_user';
+UPDATE aowow_config SET `comment` = 'if auth mode is not self; link to external account creation' WHERE `key` = 'acc_ext_create_url';
+UPDATE aowow_config SET `comment` = 'if auth mode is not self; link to external account recovery' WHERE `key` = 'acc_ext_recover_url';
+UPDATE aowow_config SET `comment` = 'php sessions are saved here. Leave empty to use php default directory.' WHERE `key` = 'session_cache_dir';
+UPDATE aowow_config SET `comment` = 'max results for search', `default` = '500' WHERE `key` = 'sql_limit_search';
+UPDATE aowow_config SET `comment` = 'max results for listviews', `default` = '300' WHERE `key` = 'sql_limit_default';
+UPDATE aowow_config SET `comment` = 'max results for suggestions', `default` = '10' WHERE `key` = 'sql_limit_quicksearch';
+UPDATE aowow_config SET `comment` = 'unlimited results (i wouldn\'t change that mate)', `default` = '0' WHERE `key` = 'sql_limit_none';
+UPDATE aowow_config SET `comment` = 'time to live for RSS (in seconds)', `default` = '60' WHERE `key` = 'ttl_rss';
+UPDATE aowow_config SET `comment` = 'disable cache, enable error_reporting - 0:None, 1:Error, 2:Warning, 3:Info', `default` = '0', `flags` = 145 WHERE `key` = 'debug';
+UPDATE aowow_config SET `comment` = 'display brb gnomes and block access for non-staff', `default` = '0' WHERE `key` = 'maintenance';
+UPDATE aowow_config SET `comment` = 'vote limit per day', `default` = '50' WHERE `key` = 'user_max_votes';
+UPDATE aowow_config SET `comment` = 'enforce SSL, if auto-detect fails', `default` = '0' WHERE `key` = 'force_ssl';
+UPDATE aowow_config SET `comment` = 'allowed locales - 0:English, 2:French, 3:German, 4:Chinese, 6:Spanish, 8:Russian', `default` = '0x15D' WHERE `key` = 'locales';
+UPDATE aowow_config SET `comment` = 'minimum dimensions of uploaded screenshots in px (yes, it\'s square)', `default` = '200' WHERE `key` = 'screenshot_min_size';
+UPDATE aowow_config SET `comment` = 'time to keep cache in seconds', `default` = '60 * 60 * 7' WHERE `key` = 'cache_decay';
+UPDATE aowow_config SET `comment` = 'set cache method - 0:filecache, 1:memcached', `default` = '1' WHERE `key` = 'cache_mode';
+UPDATE aowow_config SET `comment` = 'generated pages are saved here (requires CACHE_MODE: filecache)', `default` = 'cache/template' WHERE `key` = 'cache_dir';
+UPDATE aowow_config SET `comment` = 'how long an account is closed after exceeding FAILED_AUTH_COUNT (in seconds)', `default` = '15 * 60' WHERE `key` = 'acc_failed_auth_block';
+UPDATE aowow_config SET `comment` = 'how often invalid passwords are tolerated', `default` = '5' WHERE `key` = 'acc_failed_auth_count';
+UPDATE aowow_config SET `comment` = 'allow/disallow account creation (requires AUTH_MODE: aowow)', `default` = '1' WHERE `key` = 'acc_allow_register';
+UPDATE aowow_config SET `comment` = 'source to auth against - 0:AoWoW, 1:TC auth-table, 2:External script (config/extAuth.php)', `default` = '0', `flags`= `flags`| 256 WHERE `key` = 'acc_auth_mode';
+UPDATE aowow_config SET `comment` = 'time in wich an unconfirmed account cannot be overwritten by new registrations', `default` = '604800' WHERE `key` = 'acc_create_save_decay';
+UPDATE aowow_config SET `comment` = 'time to recover your account and new recovery requests are blocked', `default` = '300' WHERE `key` = 'acc_recovery_decay';
+UPDATE aowow_config SET `comment` = 'non-permanent session times out in time() + X', `default` = '60 * 60' WHERE `key` = 'session_timeout_delay';
+UPDATE aowow_config SET `comment` = 'lifetime of session data', `default` = '7 * 24 * 60 * 60' WHERE `key` = 'session.gc_maxlifetime';
+UPDATE aowow_config SET `comment` = 'probability to remove session data on garbage collection', `default` = '0' WHERE `key` = 'session.gc_probability';
+UPDATE aowow_config SET `comment` = 'probability to remove session data on garbage collection', `default` = '100' WHERE `key` = 'session.gc_divisor';
+UPDATE aowow_config SET `comment` = 'required reputation to upvote comments', `default` = '125' WHERE `key` = 'rep_req_upvote';
+UPDATE aowow_config SET `comment` = 'required reputation to downvote comments', `default` = '250' WHERE `key` = 'rep_req_downvote';
+UPDATE aowow_config SET `comment` = 'required reputation to write a comment', `default` = '75' WHERE `key` = 'rep_req_comment';
+UPDATE aowow_config SET `comment` = 'required reputation to write a reply', `default` = '75' WHERE `key` = 'rep_req_reply';
+UPDATE aowow_config SET `comment` = 'required reputation for double vote effect', `default` = '2500' WHERE `key` = 'rep_req_supervote';
+UPDATE aowow_config SET `comment` = 'gains more votes past this threshold', `default` = '2000' WHERE `key` = 'rep_req_votemore_base';
+UPDATE aowow_config SET `comment` = 'activated an account', `default` = '100' WHERE `key` = 'rep_reward_register';
+UPDATE aowow_config SET `comment` = 'comment received upvote', `default` = '5' WHERE `key` = 'rep_reward_upvoted';
+UPDATE aowow_config SET `comment` = 'comment received downvote', `default` = '0' WHERE `key` = 'rep_reward_downvoted';
+UPDATE aowow_config SET `comment` = 'filed an accepted report', `default` = '10' WHERE `key` = 'rep_reward_good_report';
+UPDATE aowow_config SET `comment` = 'filed a rejected report', `default` = '0' WHERE `key` = 'rep_reward_bad_report';
+UPDATE aowow_config SET `comment` = 'daily visit', `default` = '5' WHERE `key` = 'rep_reward_dailyvisit';
+UPDATE aowow_config SET `comment` = 'moderator imposed a warning', `default` = '-50' WHERE `key` = 'rep_reward_user_warned';
+UPDATE aowow_config SET `comment` = 'created a comment (not a reply)', `default` = '1' WHERE `key` = 'rep_reward_comment';
+UPDATE aowow_config SET `comment` = 'required reputation for premium status through reputation', `default` = '25000' WHERE `key` = 'rep_req_premium';
+UPDATE aowow_config SET `comment` = 'suggested / uploaded video / screenshot was approved', `default` = '10' WHERE `key` = 'rep_reward_upload';
+UPDATE aowow_config SET `comment` = 'submitted an approved article/guide', `default` = '100' WHERE `key` = 'rep_reward_article';
+UPDATE aowow_config SET `comment` = 'moderator revoked rights', `default` = '-200' WHERE `key` = 'rep_reward_user_suspended';
+UPDATE aowow_config SET `comment` = 'required reputation per additional vote past threshold', `default` = '250' WHERE `key` = 'rep_req_votemore_add';
+UPDATE aowow_config SET `comment` = 'parsing spell.dbc is quite intense', `default` = '1500M' WHERE `key` = 'memory_limit';
+UPDATE aowow_config SET `comment` = 'enable/disable profiler feature', `default` = '0', `flags`= `flags`| 256 WHERE `key` = 'profiler_enable';
+UPDATE aowow_config SET `comment` = 'min. delay between queue cycles (in ms)', `default` = '3000' WHERE `key` = 'profiler_queue_delay';
+UPDATE aowow_config SET `comment` = 'how often the javascript asks for for updates, when queued (in ms)', `default` = '5000' WHERE `key` = 'profiler_resync_ping';
+UPDATE aowow_config SET `comment` = 'how often a character can be refreshed (in sec)', `default` = '1 * 60 * 60' WHERE `key` = 'profiler_resync_delay';
diff --git a/setup/sql/updates/1717354214_01.sql b/setup/sql/updates/1717354214_01.sql
new file mode 100644
index 00000000..4d2f93c6
--- /dev/null
+++ b/setup/sql/updates/1717354214_01.sql
@@ -0,0 +1,4 @@
+ALTER TABLE aowow_creature
+ ADD KEY `idx_loot` (`lootId`),
+ ADD KEY `idx_pickpocketloot` (`pickpocketLootId`),
+ ADD KEY `idx_skinloot` (`skinLootId`);
diff --git a/setup/sql/updates/1717513011_01.sql b/setup/sql/updates/1717513011_01.sql
new file mode 100644
index 00000000..777d40bd
--- /dev/null
+++ b/setup/sql/updates/1717513011_01.sql
@@ -0,0 +1 @@
+UPDATE aowow_config SET `flags` = `flags`| 0x400 WHERE `key` IN ('locales', 'acc_auth_mode', 'profiler_enable');
diff --git a/setup/sql/updates/1718468660_01.sql b/setup/sql/updates/1718468660_01.sql
new file mode 100644
index 00000000..ba6fef73
--- /dev/null
+++ b/setup/sql/updates/1718468660_01.sql
@@ -0,0 +1,91 @@
+ALTER TABLE `aowow_itemset`
+ DROP COLUMN `bonusParsed`;
+
+DROP TABLE IF EXISTS `aowow_item_stats`;
+CREATE TABLE `aowow_item_stats` (
+ `type` smallint(5) unsigned NOT NULL,
+ `typeId` mediumint(8) NOT NULL,
+ `nsockets` tinyint(3) unsigned NULL,
+ `dps` float(8,2) NULL,
+ `damagetype` tinyint(4) NULL,
+ `dmgmin1` mediumint(5) unsigned NULL,
+ `dmgmax1` mediumint(5) unsigned NULL,
+ `speed` float(8,2) NULL,
+ `mledps` float(8,2) NULL,
+ `mledmgmin` mediumint(5) unsigned NULL,
+ `mledmgmax` mediumint(5) unsigned NULL,
+ `mlespeed` float(8,2) NULL,
+ `rgddps` float(8,2) NULL,
+ `rgddmgmin` mediumint(5) unsigned NULL,
+ `rgddmgmax` mediumint(5) unsigned NULL,
+ `rgdspeed` float(8,2) NULL,
+ `dmg` float(8,2) NULL,
+ `mana` mediumint(6) NULL,
+ `health` mediumint(6) NULL,
+ `agi` mediumint(6) NULL,
+ `str` mediumint(6) NULL,
+ `int` mediumint(6) NULL,
+ `spi` mediumint(6) NULL,
+ `sta` mediumint(6) NULL,
+ `energy` mediumint(6) NULL,
+ `rage` mediumint(6) NULL,
+ `focus` mediumint(6) NULL,
+ `runic` mediumint(6) NULL,
+ `defrtng` mediumint(6) NULL,
+ `dodgertng` mediumint(6) NULL,
+ `parryrtng` mediumint(6) NULL,
+ `blockrtng` mediumint(6) NULL,
+ `mlehitrtng` mediumint(6) NULL,
+ `rgdhitrtng` mediumint(6) NULL,
+ `splhitrtng` mediumint(6) NULL,
+ `mlecritstrkrtng` mediumint(6) NULL,
+ `rgdcritstrkrtng` mediumint(6) NULL,
+ `splcritstrkrtng` mediumint(6) NULL,
+ `_mlehitrtng` mediumint(6) NULL,
+ `_rgdhitrtng` mediumint(6) NULL,
+ `_splhitrtng` mediumint(6) NULL,
+ `_mlecritstrkrtng` mediumint(6) NULL,
+ `_rgdcritstrkrtng` mediumint(6) NULL,
+ `_splcritstrkrtng` mediumint(6) NULL,
+ `mlehastertng` mediumint(6) NULL,
+ `rgdhastertng` mediumint(6) NULL,
+ `splhastertng` mediumint(6) NULL,
+ `hitrtng` mediumint(6) NULL,
+ `critstrkrtng` mediumint(6) NULL,
+ `_hitrtng` mediumint(6) NULL,
+ `_critstrkrtng` mediumint(6) NULL,
+ `resirtng` mediumint(6) NULL,
+ `hastertng` mediumint(6) NULL,
+ `exprtng` mediumint(6) NULL,
+ `atkpwr` mediumint(6) NULL,
+ `mleatkpwr` mediumint(6) NULL,
+ `rgdatkpwr` mediumint(6) NULL,
+ `feratkpwr` mediumint(6) NULL,
+ `splheal` mediumint(6) NULL,
+ `spldmg` mediumint(6) NULL,
+ `manargn` mediumint(6) NULL,
+ `armorpenrtng` mediumint(6) NULL,
+ `splpwr` mediumint(6) NULL,
+ `healthrgn` mediumint(6) NULL,
+ `splpen` mediumint(6) NULL,
+ `block` mediumint(6) NULL,
+ `mastrtng` mediumint(6) NULL,
+ `armor` mediumint(6) NULL,
+ `armorbonus` mediumint(6) NULL,
+ `firres` mediumint(6) NULL,
+ `frores` mediumint(6) NULL,
+ `holres` mediumint(6) NULL,
+ `shares` mediumint(6) NULL,
+ `natres` mediumint(6) NULL,
+ `arcres` mediumint(6) NULL,
+ `firsplpwr` mediumint(6) NULL,
+ `frosplpwr` mediumint(6) NULL,
+ `holsplpwr` mediumint(6) NULL,
+ `shasplpwr` mediumint(6) NULL,
+ `natsplpwr` mediumint(6) NULL,
+ `arcsplpwr` mediumint(6) NULL,
+ PRIMARY KEY (`type`,`typeId`)
+) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+
+UPDATE `aowow_dbversion`
+ SET `sql` = CONCAT(IFNULL(`sql`, ''), ' item_stats');
diff --git a/setup/sql/updates/1718629021_01.sql b/setup/sql/updates/1718629021_01.sql
new file mode 100644
index 00000000..3201b8dd
--- /dev/null
+++ b/setup/sql/updates/1718629021_01.sql
@@ -0,0 +1,20 @@
+DROP TABLE IF EXISTS aowow_areatrigger;
+CREATE TABLE aowow_areatrigger (
+ `id` int unsigned NOT NULL,
+ `cuFlags` int unsigned NOT NULL DEFAULT 0 COMMENT 'see defines.php for flags',
+ `type` smallint unsigned NOT NULL,
+ `mapId` smallint unsigned NOT NULL COMMENT 'world pos. from dbc',
+ `posX` float NOT NULL COMMENT 'world pos. from dbc',
+ `posY` float NOT NULL COMMENT 'world pos. from dbc',
+ `orientation` float NOT NULL,
+ `name` varchar(100) NULL DEFAULT NULL,
+ `quest` mediumint unsigned NULL DEFAULT NULL,
+ `teleportA` smallint unsigned NULL DEFAULT NULL,
+ `teleportX` float NULL DEFAULT NULL,
+ `teleportY` float NULL DEFAULT NULL,
+ `teleportO` float NULL DEFAULT NULL,
+ `teleportF` tinyint unsigned NULL DEFAULT NULL,
+ PRIMARY KEY (`id`),
+ INDEX `quest` (`quest`),
+ INDEX `type` (`type`)
+) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4 COLLATE='utf8mb4_general_ci' ;
diff --git a/setup/updates/1647813287_01.sql b/setup/sql/updates/1718998554_01.sql
similarity index 100%
rename from setup/updates/1647813287_01.sql
rename to setup/sql/updates/1718998554_01.sql
diff --git a/setup/sql/updates/1719333848_01.sql b/setup/sql/updates/1719333848_01.sql
new file mode 100644
index 00000000..d6462672
--- /dev/null
+++ b/setup/sql/updates/1719333848_01.sql
@@ -0,0 +1,3 @@
+DROP TABLE IF EXISTS dbc_areatrigger, dbc_soundemitters;
+
+UPDATE `aowow_dbversion` SET `sql` = CONCAT(IFNULL(`sql`, ''), ' areatrigger soundemitters spawns');
diff --git a/setup/sql/updates/1719445945_01.sql b/setup/sql/updates/1719445945_01.sql
new file mode 100644
index 00000000..67b34418
--- /dev/null
+++ b/setup/sql/updates/1719445945_01.sql
@@ -0,0 +1,25 @@
+DROP TABLE IF EXISTS `aowow_areatrigger`;
+CREATE TABLE `aowow_areatrigger` (
+ `id` int unsigned NOT NULL,
+ `cuFlags` int unsigned NOT NULL DEFAULT 0 COMMENT 'see defines.php for flags',
+ `type` smallint unsigned NOT NULL,
+ `mapId` smallint unsigned NOT NULL COMMENT 'world pos. from dbc',
+ `posX` float NOT NULL COMMENT 'world pos. from dbc',
+ `posY` float NOT NULL COMMENT 'world pos. from dbc',
+ `orientation` float NOT NULL,
+ `name` varchar(100) DEFAULT NULL,
+ `quest` mediumint unsigned DEFAULT NULL,
+ PRIMARY KEY (`id`),
+ KEY `quest` (`quest`),
+ KEY `type` (`type`)
+) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
+
+ALTER TABLE `aowow_zones`
+ CHANGE COLUMN `parentAreaId` `parentMapId` smallint unsigned NOT NULL;
+
+DELETE FROM aowow_setup_custom_data WHERE
+ `command` = 'zones' AND
+ `entry` IN (3456, 3845, 3847, 3848, 3849) AND
+ `field` IN ('parentAreaId', 'parentX', 'parentY');
+
+UPDATE `aowow_dbversion` SET `sql` = CONCAT(IFNULL(`sql`, ''), ' areatrigger zones spawns');
diff --git a/setup/sql/updates/1720025031_01.sql b/setup/sql/updates/1720025031_01.sql
new file mode 100644
index 00000000..0f2ca5f1
--- /dev/null
+++ b/setup/sql/updates/1720025031_01.sql
@@ -0,0 +1,60 @@
+ALTER TABLE `aowow_account_bannedips` ENGINE=InnoDB ROW_FORMAT=DEFAULT;
+ALTER TABLE `aowow_achievement` ENGINE=InnoDB ROW_FORMAT=DEFAULT;
+ALTER TABLE `aowow_achievementcategory` ENGINE=InnoDB ROW_FORMAT=DEFAULT;
+ALTER TABLE `aowow_achievementcriteria` ENGINE=InnoDB ROW_FORMAT=DEFAULT;
+ALTER TABLE `aowow_announcements` ENGINE=InnoDB ROW_FORMAT=DEFAULT;
+ALTER TABLE `aowow_areatrigger` ENGINE=InnoDB ROW_FORMAT=DEFAULT;
+ALTER TABLE `aowow_articles` ENGINE=InnoDB ROW_FORMAT=DEFAULT;
+ALTER TABLE `aowow_classes` ENGINE=InnoDB ROW_FORMAT=DEFAULT;
+ALTER TABLE `aowow_config` ENGINE=InnoDB ROW_FORMAT=DEFAULT;
+ALTER TABLE `aowow_creature` ENGINE=InnoDB ROW_FORMAT=DEFAULT;
+ALTER TABLE `aowow_creature_waypoints` ENGINE=InnoDB ROW_FORMAT=DEFAULT;
+ALTER TABLE `aowow_currencies` ENGINE=InnoDB ROW_FORMAT=DEFAULT;
+ALTER TABLE `aowow_dbversion` ENGINE=InnoDB ROW_FORMAT=DEFAULT;
+ALTER TABLE `aowow_declinedword` ENGINE=InnoDB ROW_FORMAT=DEFAULT;
+ALTER TABLE `aowow_declinedwordcases` ENGINE=InnoDB ROW_FORMAT=DEFAULT;
+ALTER TABLE `aowow_emotes` ENGINE=InnoDB ROW_FORMAT=DEFAULT;
+ALTER TABLE `aowow_emotes_aliasses` ENGINE=InnoDB ROW_FORMAT=DEFAULT;
+ALTER TABLE `aowow_errors` ENGINE=InnoDB ROW_FORMAT=DEFAULT;
+ALTER TABLE `aowow_events` ENGINE=InnoDB ROW_FORMAT=DEFAULT;
+ALTER TABLE `aowow_factions` ENGINE=InnoDB ROW_FORMAT=DEFAULT;
+ALTER TABLE `aowow_factiontemplate` ENGINE=InnoDB ROW_FORMAT=DEFAULT;
+ALTER TABLE `aowow_glyphproperties` ENGINE=InnoDB ROW_FORMAT=DEFAULT;
+ALTER TABLE `aowow_holidays` ENGINE=InnoDB ROW_FORMAT=DEFAULT;
+ALTER TABLE `aowow_item_stats` ENGINE=InnoDB ROW_FORMAT=DEFAULT;
+ALTER TABLE `aowow_itemenchantment` ENGINE=InnoDB ROW_FORMAT=DEFAULT;
+ALTER TABLE `aowow_itemenchantmentcondition` ENGINE=InnoDB ROW_FORMAT=DEFAULT;
+ALTER TABLE `aowow_itemextendedcost` ENGINE=InnoDB ROW_FORMAT=DEFAULT;
+ALTER TABLE `aowow_itemlimitcategory` ENGINE=InnoDB ROW_FORMAT=DEFAULT;
+ALTER TABLE `aowow_itemrandomenchant` ENGINE=InnoDB ROW_FORMAT=DEFAULT;
+ALTER TABLE `aowow_itemrandomproppoints` ENGINE=InnoDB ROW_FORMAT=DEFAULT;
+ALTER TABLE `aowow_items` ENGINE=InnoDB ROW_FORMAT=DEFAULT;
+ALTER TABLE `aowow_itemset` ENGINE=InnoDB ROW_FORMAT=DEFAULT;
+ALTER TABLE `aowow_lock` ENGINE=InnoDB ROW_FORMAT=DEFAULT;
+ALTER TABLE `aowow_loot_link` ENGINE=InnoDB ROW_FORMAT=DEFAULT;
+ALTER TABLE `aowow_mails` ENGINE=InnoDB ROW_FORMAT=DEFAULT;
+ALTER TABLE `aowow_objects` ENGINE=InnoDB ROW_FORMAT=DEFAULT;
+ALTER TABLE `aowow_pet` ENGINE=InnoDB ROW_FORMAT=DEFAULT;
+ALTER TABLE `aowow_quests` ENGINE=InnoDB ROW_FORMAT=DEFAULT;
+ALTER TABLE `aowow_quests_startend` ENGINE=InnoDB ROW_FORMAT=DEFAULT;
+ALTER TABLE `aowow_races` ENGINE=InnoDB ROW_FORMAT=DEFAULT;
+ALTER TABLE `aowow_reports` ENGINE=InnoDB ROW_FORMAT=DEFAULT;
+ALTER TABLE `aowow_scalingstatdistribution` ENGINE=InnoDB ROW_FORMAT=DEFAULT;
+ALTER TABLE `aowow_scalingstatvalues` ENGINE=InnoDB ROW_FORMAT=DEFAULT;
+ALTER TABLE `aowow_setup_custom_data` ENGINE=InnoDB ROW_FORMAT=DEFAULT;
+ALTER TABLE `aowow_shapeshiftforms` ENGINE=InnoDB ROW_FORMAT=DEFAULT;
+ALTER TABLE `aowow_skillline` ENGINE=InnoDB ROW_FORMAT=DEFAULT;
+ALTER TABLE `aowow_spawns` ENGINE=InnoDB ROW_FORMAT=DEFAULT;
+ALTER TABLE `aowow_spawns_override` ENGINE=InnoDB ROW_FORMAT=DEFAULT;
+ALTER TABLE `aowow_spell` ENGINE=InnoDB ROW_FORMAT=DEFAULT;
+ALTER TABLE `aowow_spelldifficulty` ENGINE=InnoDB ROW_FORMAT=DEFAULT;
+ALTER TABLE `aowow_spellfocusobject` ENGINE=InnoDB ROW_FORMAT=DEFAULT;
+ALTER TABLE `aowow_spelloverride` ENGINE=InnoDB ROW_FORMAT=DEFAULT;
+ALTER TABLE `aowow_spellrange` ENGINE=InnoDB ROW_FORMAT=DEFAULT;
+ALTER TABLE `aowow_spellvariables` ENGINE=InnoDB ROW_FORMAT=DEFAULT;
+ALTER TABLE `aowow_talents` ENGINE=InnoDB ROW_FORMAT=DEFAULT;
+ALTER TABLE `aowow_taxinodes` ENGINE=InnoDB ROW_FORMAT=DEFAULT;
+ALTER TABLE `aowow_taxipath` ENGINE=InnoDB ROW_FORMAT=DEFAULT;
+ALTER TABLE `aowow_titles` ENGINE=InnoDB ROW_FORMAT=DEFAULT;
+ALTER TABLE `aowow_totemcategory` ENGINE=InnoDB ROW_FORMAT=DEFAULT;
+ALTER TABLE `aowow_zones` ENGINE=InnoDB ROW_FORMAT=DEFAULT;
diff --git a/setup/sql/updates/1720116490_01.sql b/setup/sql/updates/1720116490_01.sql
new file mode 100644
index 00000000..10c0988c
--- /dev/null
+++ b/setup/sql/updates/1720116490_01.sql
@@ -0,0 +1,217 @@
+DROP TABLE IF EXISTS `aowow_loot_link`;
+CREATE TABLE `aowow_loot_link` (
+ `npcId` mediumint(9) unsigned NOT NULL,
+ `objectId` mediumint(8) unsigned NOT NULL,
+ `difficulty` tinyint(3) unsigned NOT NULL DEFAULT 1,
+ `priority` tinyint(3) unsigned NOT NULL COMMENT '1: use this npc from group encounter (others 0)',
+ `encounterId` mediumint(8) unsigned NOT NULL COMMENT 'as title reference',
+ UNIQUE KEY `npcId_difficulty` (`npcId`, `difficulty`),
+ KEY `objectId` (`objectId`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+
+INSERT INTO `aowow_loot_link` VALUES
+ (19710,184465,1,0,0),
+ (19218,184465,1,1,0),
+ (21526,184849,2,0,0),
+ (21525,184849,2,1,0),
+ (17537,185168,1,1,0),
+ (17536,185168,1,0,0),
+ (18434,185169,2,1,0),
+ (18432,185169,2,0,0),
+ (28234,190586,1,0,0),
+ (28234,193996,2,0,0),
+ (26533,190663,1,0,0),
+ (31217,193597,2,0,0),
+ (27656,191349,1,0,0),
+ (31561,193603,2,0,0),
+ (28859,193905,1,0,0),
+ (31734,193967,2,0,0),
+ (32845,194307,1,0,0),
+ (32846,194308,2,0,0),
+ (32845,194200,3,0,0),
+ (32846,194201,4,0,0),
+ (32865,194312,1,0,0),
+ (33147,194314,2,0,0),
+ (32865,194313,3,0,0),
+ (33147,194315,4,0,0),
+ (32906,194324,1,0,0),
+ (33360,194328,2,0,0),
+ (32906,194327,3,0,0),
+ (33360,194331,4,0,0),
+ (32871,194821,1,0,0),
+ (33070,194822,2,0,0),
+ (33350,194789,1,0,0),
+ (33350,194956,2,0,0),
+ (33350,194957,3,0,0),
+ (33350,194958,4,0,0),
+ (32930,195046,1,0,0),
+ (33909,195047,2,0,0),
+ (34928,195323,1,0,0),
+ (35517,195324,2,0,0),
+ (35119,195374,1,0,0),
+ (35518,195375,2,0,0),
+ (37226,201710,1,0,0),
+ (37226,202336,2,0,0),
+ (36789,201959,1,0,0),
+ (38174,202339,2,0,0),
+ (36789,202338,3,0,0),
+ (38174,202340,4,0,0),
+ (38402,202239,1,0,0),
+ (38582,202240,2,0,0),
+ (37813,202238,3,0,0),
+ (38583,202241,4,0,0),
+ (9034,169243,1,0,243),
+ (9035,169243,1,1,243),
+ (9039,169243,1,0,243),
+ (9036,169243,1,0,243),
+ (9037,169243,1,0,243),
+ (9038,169243,1,0,243),
+ (9040,169243,1,0,243),
+ (34657,195709,1,0,334),
+ (34701,195709,1,0,334),
+ (34703,195709,1,0,334),
+ (34702,195709,1,0,334),
+ (34705,195709,1,0,334),
+ (35571,195709,1,0,334),
+ (35617,195709,1,0,334),
+ (35572,195709,1,0,334),
+ (35570,195709,1,0,334),
+ (35569,195709,1,1,334),
+ (36089,195710,2,0,334),
+ (36086,195710,2,0,334),
+ (36087,195710,2,0,334),
+ (36082,195710,2,0,334),
+ (36085,195710,2,1,334),
+ (36088,195710,2,0,334),
+ (36084,195710,2,0,334),
+ (36083,195710,2,0,334),
+ (36091,195710,2,0,334),
+ (36090,195710,2,0,334),
+ (34458,195631,1,0,637),
+ (34465,195631,1,0,637),
+ (34463,195631,1,0,637),
+ (34460,195631,1,0,637),
+ (34459,195631,1,0,637),
+ (34456,195631,1,0,637),
+ (34466,195631,1,0,637),
+ (34467,195631,1,0,637),
+ (34468,195631,1,0,637),
+ (34469,195631,1,0,637),
+ (34470,195631,1,0,637),
+ (34472,195631,1,0,637),
+ (34474,195631,1,0,637),
+ (34473,195631,1,0,637),
+ (34455,195631,1,0,637),
+ (34454,195631,1,0,637),
+ (34453,195631,1,0,637),
+ (34441,195631,1,1,637),
+ (34471,195631,1,0,637),
+ (34475,195631,1,0,637),
+ (34444,195631,1,0,637),
+ (34445,195631,1,0,637),
+ (34447,195631,1,0,637),
+ (34461,195631,1,0,637),
+ (34448,195631,1,0,637),
+ (34449,195631,1,0,637),
+ (34450,195631,1,0,637),
+ (34451,195631,1,0,637),
+ (35686,195632,2,0,637),
+ (35671,195632,2,0,637),
+ (35683,195632,2,0,637),
+ (35680,195632,2,0,637),
+ (35674,195632,2,0,637),
+ (35689,195632,2,0,637),
+ (35721,195632,2,0,637),
+ (35718,195632,2,0,637),
+ (35731,195632,2,0,637),
+ (35714,195632,2,0,637),
+ (35711,195632,2,0,637),
+ (35734,195632,2,0,637),
+ (35737,195632,2,0,637),
+ (35740,195632,2,0,637),
+ (35743,195632,2,0,637),
+ (35746,195632,2,0,637),
+ (35708,195632,2,0,637),
+ (35705,195632,2,0,637),
+ (35702,195632,2,0,637),
+ (35699,195632,2,0,637),
+ (35695,195632,2,0,637),
+ (35692,195632,2,0,637),
+ (35728,195632,2,0,637),
+ (35724,195632,2,0,637),
+ (35668,195632,2,0,637),
+ (34442,195632,2,1,637),
+ (35662,195632,2,0,637),
+ (35665,195632,2,0,637),
+ (35725,195633,3,0,637),
+ (35722,195633,3,0,637),
+ (35719,195633,3,0,637),
+ (35715,195633,3,0,637),
+ (35709,195633,3,0,637),
+ (35706,195633,3,0,637),
+ (35729,195633,3,0,637),
+ (35744,195633,3,0,637),
+ (35732,195633,3,0,637),
+ (35735,195633,3,0,637),
+ (35738,195633,3,0,637),
+ (35741,195633,3,0,637),
+ (35747,195633,3,0,637),
+ (35712,195633,3,0,637),
+ (35703,195633,3,0,637),
+ (35700,195633,3,0,637),
+ (35672,195633,3,0,637),
+ (35690,195633,3,0,637),
+ (35687,195633,3,0,637),
+ (35669,195633,3,0,637),
+ (35684,195633,3,0,637),
+ (35693,195633,3,0,637),
+ (34443,195633,3,1,637),
+ (35681,195633,3,0,637),
+ (35663,195633,3,0,637),
+ (35666,195633,3,0,637),
+ (35696,195633,3,0,637),
+ (35675,195633,3,0,637),
+ (35700,195635,4,0,637),
+ (35749,195635,4,1,637),
+ (35706,195635,4,0,637),
+ (35703,195635,4,0,637),
+ (35709,195635,4,0,637),
+ (35744,195635,4,0,637),
+ (35741,195635,4,0,637),
+ (35735,195635,4,0,637),
+ (35732,195635,4,0,637),
+ (35729,195635,4,0,637),
+ (35725,195635,4,0,637),
+ (35722,195635,4,0,637),
+ (35719,195635,4,0,637),
+ (35715,195635,4,0,637),
+ (35712,195635,4,0,637),
+ (35747,195635,4,0,637),
+ (35696,195635,4,0,637),
+ (35675,195635,4,0,637),
+ (35681,195635,4,0,637),
+ (35663,195635,4,0,637),
+ (35669,195635,4,0,637),
+ (35666,195635,4,0,637),
+ (35738,195635,4,0,637),
+ (35672,195635,4,0,637),
+ (35684,195635,4,0,637),
+ (35687,195635,4,0,637),
+ (35690,195635,4,0,637),
+ (35693,195635,4,0,637),
+ (16064,181366,1,0,692),
+ (16065,181366,1,0,692),
+ (16063,181366,1,0,692),
+ (30549,181366,1,1,692),
+ (30602,193426,2,0,692),
+ (30603,193426,2,0,692),
+ (30601,193426,2,0,692),
+ (30600,193426,2,1,692),
+ (36948,202178,1,0,847),
+ (36939,202178,1,0,847),
+ (38157,202180,2,0,847),
+ (38156,202180,2,0,847),
+ (38639,202177,3,0,847),
+ (38637,202177,3,0,847),
+ (38640,202179,4,0,847),
+ (38638,202179,4,0,847);
diff --git a/setup/sql/updates/1720385207_01.sql b/setup/sql/updates/1720385207_01.sql
new file mode 100644
index 00000000..2f84c768
--- /dev/null
+++ b/setup/sql/updates/1720385207_01.sql
@@ -0,0 +1,4 @@
+DELETE FROM `aowow_loot_link` WHERE `npcId` IN (25740,26338,12018);
+INSERT INTO `aowow_loot_link` VALUES
+ (25740,187892,0,0,0),
+ (12018,179703,0,0,0);
diff --git a/setup/sql/updates/1720448853_01.sql b/setup/sql/updates/1720448853_01.sql
new file mode 100644
index 00000000..6436fe43
--- /dev/null
+++ b/setup/sql/updates/1720448853_01.sql
@@ -0,0 +1,2 @@
+ALTER TABLE `aowow_setup_custom_data`
+ MODIFY COLUMN `value` text DEFAULT NULL;
diff --git a/setup/sql/updates/1720449931_01.sql b/setup/sql/updates/1720449931_01.sql
new file mode 100644
index 00000000..af72739b
--- /dev/null
+++ b/setup/sql/updates/1720449931_01.sql
@@ -0,0 +1 @@
+UPDATE `aowow_dbversion` SET `build` = CONCAT(IFNULL(`build`, ''), ' realmmenu');
diff --git a/setup/sql/updates/1720451578_01.sql b/setup/sql/updates/1720451578_01.sql
new file mode 100644
index 00000000..05728e9c
--- /dev/null
+++ b/setup/sql/updates/1720451578_01.sql
@@ -0,0 +1 @@
+UPDATE `aowow_config` SET `flags` = 1441 WHERE `key` = 'locales';
diff --git a/setup/sql/updates/1720455278_01.sql b/setup/sql/updates/1720455278_01.sql
new file mode 100644
index 00000000..88b450bb
--- /dev/null
+++ b/setup/sql/updates/1720455278_01.sql
@@ -0,0 +1,73 @@
+SET FOREIGN_KEY_CHECKS=0;
+
+DROP TABLE IF EXISTS `aowow_profiler_completion_quests`;
+CREATE TABLE `aowow_profiler_completion_quests` (
+`id` int unsigned NOT NULL,
+`questId` mediumint unsigned NOT NULL,
+KEY `id` (`id`),
+CONSTRAINT `FK_pr_completion_quests` FOREIGN KEY (`id`) REFERENCES `aowow_profiler_profiles` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+
+DROP TABLE IF EXISTS `aowow_profiler_completion_skills`;
+CREATE TABLE `aowow_profiler_completion_skills` (
+`id` int unsigned NOT NULL,
+`skillId` smallint unsigned NOT NULL,
+`value` smallint unsigned DEFAULT NULL,
+`max` smallint unsigned DEFAULT NULL,
+KEY `id` (`id`),
+KEY `typeId` (`skillId`),
+CONSTRAINT `FK_pr_completion_skills` FOREIGN KEY (`id`) REFERENCES `aowow_profiler_profiles` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+
+DROP TABLE IF EXISTS `aowow_profiler_completion_reputation`;
+CREATE TABLE `aowow_profiler_completion_reputation` (
+`id` int unsigned NOT NULL,
+`factionId` smallint unsigned NOT NULL,
+`standing` mediumint DEFAULT NULL,
+KEY `id` (`id`),
+CONSTRAINT `FK_pr_completion_reputation` FOREIGN KEY (`id`) REFERENCES `aowow_profiler_profiles` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+
+DROP TABLE IF EXISTS `aowow_profiler_completion_titles`;
+CREATE TABLE `aowow_profiler_completion_titles` (
+`id` int unsigned NOT NULL,
+`titleId` tinyint unsigned NOT NULL,
+KEY `id` (`id`),
+CONSTRAINT `FK_pr_completion_titles` FOREIGN KEY (`id`) REFERENCES `aowow_profiler_profiles` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+
+DROP TABLE IF EXISTS `aowow_profiler_completion_achievements`;
+CREATE TABLE `aowow_profiler_completion_achievements` (
+`id` int unsigned NOT NULL,
+`achievementId` smallint unsigned NOT NULL,
+`date` int unsigned DEFAULT NULL,
+KEY `id` (`id`),
+KEY `typeId` (`achievementId`),
+CONSTRAINT `FK_pr_completion_achievements` FOREIGN KEY (`id`) REFERENCES `aowow_profiler_profiles` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+
+DROP TABLE IF EXISTS `aowow_profiler_completion_statistics`;
+CREATE TABLE `aowow_profiler_completion_statistics` (
+`id` int unsigned NOT NULL,
+`achievementId` smallint NOT NULL,
+`date` int unsigned DEFAULT NULL,
+`counter` smallint unsigned DEFAULT NULL, -- could be values of INT size, but surely not for bosskill counters, right? ... RIGHT!?
+KEY `id` (`id`),
+KEY `typeId` (`achievementId`),
+CONSTRAINT `FK_pr_completion_statistics` FOREIGN KEY (`id`) REFERENCES `aowow_profiler_profiles` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+
+DROP TABLE IF EXISTS `aowow_profiler_completion_spells`;
+CREATE TABLE `aowow_profiler_completion_spells` (
+`id` int unsigned NOT NULL,
+`spellId` mediumint unsigned NOT NULL,
+KEY `id` (`id`),
+CONSTRAINT `FK_pr_completion_spells` FOREIGN KEY (`id`) REFERENCES `aowow_profiler_profiles` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+
+-- force profiles to be updated
+UPDATE `aowow_profiler_profiles` SET `lastUpdated` = 0;
+
+DROP TABLE IF EXISTS `aowow_profiler_completion`;
+
+SET FOREIGN_KEY_CHECKS=1;
diff --git a/setup/sql/updates/1720523764_01.sql b/setup/sql/updates/1720523764_01.sql
new file mode 100644
index 00000000..2d588c5b
--- /dev/null
+++ b/setup/sql/updates/1720523764_01.sql
@@ -0,0 +1,3 @@
+ALTER TABLE `aowow_comments`
+ MODIFY COLUMN `type` smallint unsigned NOT NULL DEFAULT 0 COMMENT 'Type of Page',
+ MODIFY COLUMN `typeId` mediumint NOT NULL DEFAULT 0 COMMENT 'ID Of Page';
diff --git a/setup/sql/updates/1720608489_01.sql b/setup/sql/updates/1720608489_01.sql
new file mode 100644
index 00000000..cc47e011
--- /dev/null
+++ b/setup/sql/updates/1720608489_01.sql
@@ -0,0 +1,25 @@
+DROP TABLE IF EXISTS dbc_spell;
+
+ALTER TABLE `aowow_spell`
+ DROP COLUMN `effect1SpellClassMaskA`,
+ DROP COLUMN `effect2SpellClassMaskA`,
+ DROP COLUMN `effect3SpellClassMaskA`,
+ DROP COLUMN `effect1SpellClassMaskB`,
+ DROP COLUMN `effect2SpellClassMaskB`,
+ DROP COLUMN `effect3SpellClassMaskB`,
+ DROP COLUMN `effect1SpellClassMaskC`,
+ DROP COLUMN `effect2SpellClassMaskC`,
+ DROP COLUMN `effect3SpellClassMaskC`;
+
+ALTER TABLE `aowow_spell`
+ ADD COLUMN `effect1SpellClassMaskA` int NOT NULL AFTER `effect3PointsPerComboPoint`,
+ ADD COLUMN `effect1SpellClassMaskB` int NOT NULL AFTER `effect1SpellClassMaskA`,
+ ADD COLUMN `effect1SpellClassMaskC` int NOT NULL AFTER `effect1SpellClassMaskB`,
+ ADD COLUMN `effect2SpellClassMaskA` int NOT NULL AFTER `effect1SpellClassMaskC`,
+ ADD COLUMN `effect2SpellClassMaskB` int NOT NULL AFTER `effect2SpellClassMaskA`,
+ ADD COLUMN `effect2SpellClassMaskC` int NOT NULL AFTER `effect2SpellClassMaskB`,
+ ADD COLUMN `effect3SpellClassMaskA` int NOT NULL AFTER `effect2SpellClassMaskC`,
+ ADD COLUMN `effect3SpellClassMaskB` int NOT NULL AFTER `effect3SpellClassMaskA`,
+ ADD COLUMN `effect3SpellClassMaskC` int NOT NULL AFTER `effect3SpellClassMaskB`;
+
+UPDATE `aowow_dbversion` SET `sql` = CONCAT(IFNULL(`sql`, ''), ' spell');
diff --git a/setup/sql/updates/1720654746_01.sql b/setup/sql/updates/1720654746_01.sql
new file mode 100644
index 00000000..2983169a
--- /dev/null
+++ b/setup/sql/updates/1720654746_01.sql
@@ -0,0 +1 @@
+UPDATE `aowow_dbversion` SET `build` = CONCAT(IFNULL(`build`, ''), ' enchants');
diff --git a/setup/sql/updates/1720969085_01.sql b/setup/sql/updates/1720969085_01.sql
new file mode 100644
index 00000000..3d0516f5
--- /dev/null
+++ b/setup/sql/updates/1720969085_01.sql
@@ -0,0 +1,2 @@
+ALTER TABLE `aowow_errors`
+ ADD COLUMN `post` text NOT NULL AFTER `query`;
diff --git a/setup/sql/updates/1722382255_01.sql b/setup/sql/updates/1722382255_01.sql
new file mode 100644
index 00000000..9fa47c05
--- /dev/null
+++ b/setup/sql/updates/1722382255_01.sql
@@ -0,0 +1 @@
+UPDATE `aowow_dbversion` SET `sql` = CONCAT(IFNULL(`sql`, ''), ' stats');
diff --git a/setup/sql/updates/1724095916_01.sql b/setup/sql/updates/1724095916_01.sql
new file mode 100644
index 00000000..9620bdbe
--- /dev/null
+++ b/setup/sql/updates/1724095916_01.sql
@@ -0,0 +1 @@
+UPDATE `aowow_dbversion` SET `sql` = CONCAT(IFNULL(`sql`, ''), ' summonproperties');
diff --git a/setup/sql/updates/1724503538_01.sql b/setup/sql/updates/1724503538_01.sql
new file mode 100644
index 00000000..c0c38b25
--- /dev/null
+++ b/setup/sql/updates/1724503538_01.sql
@@ -0,0 +1 @@
+UPDATE `aowow_dbversion` SET `sql` = CONCAT(IFNULL(`sql`, ''), ' spawns');
diff --git a/setup/sql/updates/1725025019_01.sql b/setup/sql/updates/1725025019_01.sql
new file mode 100644
index 00000000..3c565be8
--- /dev/null
+++ b/setup/sql/updates/1725025019_01.sql
@@ -0,0 +1 @@
+UPDATE `aowow_dbversion` SET `build` = CONCAT(IFNULL(`build`, ''), ' tooltips markup locales');
diff --git a/setup/sql/updates/1725972644_01.sql b/setup/sql/updates/1725972644_01.sql
new file mode 100644
index 00000000..fa0f3d04
--- /dev/null
+++ b/setup/sql/updates/1725972644_01.sql
@@ -0,0 +1,87 @@
+DROP TABLE IF EXISTS `aowow_item_stats`;
+CREATE TABLE `aowow_item_stats` (
+ `type` smallint(5) unsigned NOT NULL,
+ `typeId` mediumint(8) NOT NULL,
+ `nsockets` tinyint(3) unsigned NOT NULL DEFAULT 0,
+ `dps` float(8,2) NULL,
+ `damagetype` tinyint(4) NULL,
+ `dmgmin1` mediumint(5) unsigned NULL,
+ `dmgmax1` mediumint(5) unsigned NULL,
+ `speed` float(8,2) NULL,
+ `mledps` float(8,2) NULL,
+ `mledmgmin` mediumint(5) unsigned NULL,
+ `mledmgmax` mediumint(5) unsigned NULL,
+ `mlespeed` float(8,2) NULL,
+ `rgddps` float(8,2) NULL,
+ `rgddmgmin` mediumint(5) unsigned NULL,
+ `rgddmgmax` mediumint(5) unsigned NULL,
+ `rgdspeed` float(8,2) NULL,
+ `dmg` float(8,2) NOT NULL DEFAULT 0,
+ `mana` mediumint(6) NOT NULL DEFAULT 0,
+ `health` mediumint(6) NOT NULL DEFAULT 0,
+ `agi` mediumint(6) NOT NULL DEFAULT 0,
+ `str` mediumint(6) NOT NULL DEFAULT 0,
+ `int` mediumint(6) NOT NULL DEFAULT 0,
+ `spi` mediumint(6) NOT NULL DEFAULT 0,
+ `sta` mediumint(6) NOT NULL DEFAULT 0,
+ `energy` mediumint(6) NOT NULL DEFAULT 0,
+ `rage` mediumint(6) NOT NULL DEFAULT 0,
+ `focus` mediumint(6) NOT NULL DEFAULT 0,
+ `runic` mediumint(6) NOT NULL DEFAULT 0,
+ `defrtng` mediumint(6) NOT NULL DEFAULT 0,
+ `dodgertng` mediumint(6) NOT NULL DEFAULT 0,
+ `parryrtng` mediumint(6) NOT NULL DEFAULT 0,
+ `blockrtng` mediumint(6) NOT NULL DEFAULT 0,
+ `mlehitrtng` mediumint(6) NOT NULL DEFAULT 0,
+ `rgdhitrtng` mediumint(6) NOT NULL DEFAULT 0,
+ `splhitrtng` mediumint(6) NOT NULL DEFAULT 0,
+ `mlecritstrkrtng` mediumint(6) NOT NULL DEFAULT 0,
+ `rgdcritstrkrtng` mediumint(6) NOT NULL DEFAULT 0,
+ `splcritstrkrtng` mediumint(6) NOT NULL DEFAULT 0,
+ `_mlehitrtng` mediumint(6) NOT NULL DEFAULT 0,
+ `_rgdhitrtng` mediumint(6) NOT NULL DEFAULT 0,
+ `_splhitrtng` mediumint(6) NOT NULL DEFAULT 0,
+ `_mlecritstrkrtng` mediumint(6) NOT NULL DEFAULT 0,
+ `_rgdcritstrkrtng` mediumint(6) NOT NULL DEFAULT 0,
+ `_splcritstrkrtng` mediumint(6) NOT NULL DEFAULT 0,
+ `mlehastertng` mediumint(6) NOT NULL DEFAULT 0,
+ `rgdhastertng` mediumint(6) NOT NULL DEFAULT 0,
+ `splhastertng` mediumint(6) NOT NULL DEFAULT 0,
+ `hitrtng` mediumint(6) NOT NULL DEFAULT 0,
+ `critstrkrtng` mediumint(6) NOT NULL DEFAULT 0,
+ `_hitrtng` mediumint(6) NOT NULL DEFAULT 0,
+ `_critstrkrtng` mediumint(6) NOT NULL DEFAULT 0,
+ `resirtng` mediumint(6) NOT NULL DEFAULT 0,
+ `hastertng` mediumint(6) NOT NULL DEFAULT 0,
+ `exprtng` mediumint(6) NOT NULL DEFAULT 0,
+ `atkpwr` mediumint(6) NOT NULL DEFAULT 0,
+ `mleatkpwr` mediumint(6) NOT NULL DEFAULT 0,
+ `rgdatkpwr` mediumint(6) NOT NULL DEFAULT 0,
+ `feratkpwr` mediumint(6) NOT NULL DEFAULT 0,
+ `splheal` mediumint(6) NOT NULL DEFAULT 0,
+ `spldmg` mediumint(6) NOT NULL DEFAULT 0,
+ `manargn` mediumint(6) NOT NULL DEFAULT 0,
+ `armorpenrtng` mediumint(6) NOT NULL DEFAULT 0,
+ `splpwr` mediumint(6) NOT NULL DEFAULT 0,
+ `healthrgn` mediumint(6) NOT NULL DEFAULT 0,
+ `splpen` mediumint(6) NOT NULL DEFAULT 0,
+ `block` mediumint(6) NOT NULL DEFAULT 0,
+ `mastrtng` mediumint(6) NOT NULL DEFAULT 0,
+ `armor` mediumint(6) NOT NULL DEFAULT 0,
+ `armorbonus` mediumint(6) NULL,
+ `firres` mediumint(6) NOT NULL DEFAULT 0,
+ `frores` mediumint(6) NOT NULL DEFAULT 0,
+ `holres` mediumint(6) NOT NULL DEFAULT 0,
+ `shares` mediumint(6) NOT NULL DEFAULT 0,
+ `natres` mediumint(6) NOT NULL DEFAULT 0,
+ `arcres` mediumint(6) NOT NULL DEFAULT 0,
+ `firsplpwr` mediumint(6) NOT NULL DEFAULT 0,
+ `frosplpwr` mediumint(6) NOT NULL DEFAULT 0,
+ `holsplpwr` mediumint(6) NOT NULL DEFAULT 0,
+ `shasplpwr` mediumint(6) NOT NULL DEFAULT 0,
+ `natsplpwr` mediumint(6) NOT NULL DEFAULT 0,
+ `arcsplpwr` mediumint(6) NOT NULL DEFAULT 0,
+ PRIMARY KEY (`type`,`typeId`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+
+UPDATE `aowow_dbversion` SET `sql` = CONCAT(IFNULL(`sql`, ''), ' stats');
diff --git a/setup/sql/updates/1741361137_01.sql b/setup/sql/updates/1741361137_01.sql
new file mode 100644
index 00000000..92ad74d7
--- /dev/null
+++ b/setup/sql/updates/1741361137_01.sql
@@ -0,0 +1 @@
+UPDATE `aowow_dbversion` SET `sql` = CONCAT(IFNULL(`sql`, ''), ' itemset'), `build` = CONCAT(IFNULL(`build`, ''), ' itemsets');
diff --git a/setup/sql/updates/1742667408_01.sql b/setup/sql/updates/1742667408_01.sql
new file mode 100644
index 00000000..dc4ff953
--- /dev/null
+++ b/setup/sql/updates/1742667408_01.sql
@@ -0,0 +1,2 @@
+UPDATE `aowow_setup_custom_data` SET `command` = 'items' WHERE `command` = 'item';
+UPDATE `aowow_dbversion` SET `sql` = CONCAT(IFNULL(`sql`, ''), ' items');
diff --git a/setup/sql/updates/1750613426_01.sql b/setup/sql/updates/1750613426_01.sql
new file mode 100644
index 00000000..4aced6c3
--- /dev/null
+++ b/setup/sql/updates/1750613426_01.sql
@@ -0,0 +1 @@
+UPDATE `aowow_dbversion` SET `sql` = CONCAT(IFNULL(`sql`, ''), ' quests');
diff --git a/setup/sql/updates/1753369288_01.sql b/setup/sql/updates/1753369288_01.sql
new file mode 100644
index 00000000..cc7a46fc
--- /dev/null
+++ b/setup/sql/updates/1753369288_01.sql
@@ -0,0 +1,27 @@
+ALTER TABLE aowow_account_weightscales
+ ADD COLUMN `orderIdx` tinyint unsigned NOT NULL DEFAULT 0 COMMENT 'check how Profiler handles classes with more than 3 specs before modifying' AFTER `class`;
+
+UPDATE aowow_account_weightscales SET `orderIdx` = 1 WHERE `userId` = 0 AND `class` = 1 AND `name` = 'fury';
+UPDATE aowow_account_weightscales SET `orderIdx` = 2 WHERE `userId` = 0 AND `class` = 1 AND `name` = 'prot';
+UPDATE aowow_account_weightscales SET `orderIdx` = 1 WHERE `userId` = 0 AND `class` = 2 AND `name` = 'prot';
+UPDATE aowow_account_weightscales SET `orderIdx` = 2 WHERE `userId` = 0 AND `class` = 2 AND `name` = 'retrib';
+UPDATE aowow_account_weightscales SET `orderIdx` = 1 WHERE `userId` = 0 AND `class` = 3 AND `name` = 'marks';
+UPDATE aowow_account_weightscales SET `orderIdx` = 2 WHERE `userId` = 0 AND `class` = 3 AND `name` = 'surv';
+UPDATE aowow_account_weightscales SET `orderIdx` = 1 WHERE `userId` = 0 AND `class` = 4 AND `name` = 'combat';
+UPDATE aowow_account_weightscales SET `orderIdx` = 2 WHERE `userId` = 0 AND `class` = 4 AND `name` = 'subtle';
+UPDATE aowow_account_weightscales SET `orderIdx` = 1 WHERE `userId` = 0 AND `class` = 5 AND `name` = 'holy';
+UPDATE aowow_account_weightscales SET `orderIdx` = 2 WHERE `userId` = 0 AND `class` = 5 AND `name` = 'shadow';
+UPDATE aowow_account_weightscales SET `orderIdx` = 1 WHERE `userId` = 0 AND `class` = 6 AND `name` = 'frostdps';
+UPDATE aowow_account_weightscales SET `orderIdx` = 2 WHERE `userId` = 0 AND `class` = 6 AND `name` = 'frosttank';
+UPDATE aowow_account_weightscales SET `orderIdx` = 3 WHERE `userId` = 0 AND `class` = 6 AND `name` = 'unholydps';
+UPDATE aowow_account_weightscales SET `orderIdx` = 1 WHERE `userId` = 0 AND `class` = 7 AND `name` = 'enhance';
+UPDATE aowow_account_weightscales SET `orderIdx` = 2 WHERE `userId` = 0 AND `class` = 7 AND `name` = 'resto';
+UPDATE aowow_account_weightscales SET `orderIdx` = 1 WHERE `userId` = 0 AND `class` = 8 AND `name` = 'fire';
+UPDATE aowow_account_weightscales SET `orderIdx` = 2 WHERE `userId` = 0 AND `class` = 8 AND `name` = 'frost';
+UPDATE aowow_account_weightscales SET `orderIdx` = 1 WHERE `userId` = 0 AND `class` = 9 AND `name` = 'demo';
+UPDATE aowow_account_weightscales SET `orderIdx` = 2 WHERE `userId` = 0 AND `class` = 9 AND `name` = 'destro';
+UPDATE aowow_account_weightscales SET `orderIdx` = 1 WHERE `userId` = 0 AND `class` = 11 AND `name` = 'feraldps';
+UPDATE aowow_account_weightscales SET `orderIdx` = 2 WHERE `userId` = 0 AND `class` = 11 AND `name` = 'feraltank';
+UPDATE aowow_account_weightscales SET `orderIdx` = 3 WHERE `userId` = 0 AND `class` = 11 AND `name` = 'resto';
+
+UPDATE `aowow_dbversion` SET `build` = CONCAT(IFNULL(`build`, ''), ' weightpresets');
diff --git a/setup/sql/updates/1753563161_01.sql b/setup/sql/updates/1753563161_01.sql
new file mode 100644
index 00000000..cae5801d
--- /dev/null
+++ b/setup/sql/updates/1753563161_01.sql
@@ -0,0 +1 @@
+UPDATE aowow_articles SET `article` = '[menu tab=2 path=2,8]\r\n\r\nSearch plugins make it easy to search the database right from your browser!\r\n\r\n[toc h3=false]\r\n\r\n[h2][img src=STATIC_URL/images/help/searchplugins/firefox.png border=0 margin=5 float=left]Firefox[/h2]\r\n\r\n[div float=right align=right][img border=2 src=STATIC_URL/images/help/searchplugins/os-firefox.png][/div]\r\n\r\n[script]\r\nfunction addPlugin()\r\n{\r\n if (typeof window.external.AddSearchProvider == "function")\r\n window.external.AddSearchProvider("STATIC_URL/download/searchplugins/aowow.xml");\r\n else\r\n alert("This feature is unavailable.");\r\n}\r\n[/script]\r\n[pad]\r\nEither\r\n[ul]\r\n[li]Click on the button below to install the search plugin in your browser or[/li]\r\n[li]Right-click your address bar and then clck on "Add AoWoW" or[/li]\r\n[li]Click on the [img src=STATIC_URL/images/icons/add.png border=0] on the browser search bar and then on [img src=STATIC_URL/images/icons/add.png border=0] "Add search engine"[/li]\r\n[/ul]\r\n\r\n[pad]\r\n[html]Install pluginInstall plugin[/html]\r\n[div clear=both][/div]\r\n\r\n[h2][img src=STATIC_URL/images/help/searchplugins/edge.png border=0 float=left][img src=STATIC_URL/images/help/searchplugins/chrome.png border=0 float=left]MS Edge / Google Chrome[/h2]\r\n\r\n[div float=right align=right][img border=2 src=STATIC_URL/images/help/searchplugins/os-edge.png][/div]\r\n[pad]\r\nFor Chrome-based browsers go to settings and fill in the add search engine form as shown.\r\n[pad]\r\n[div width=500px]\r\n[pre]HOST_URL/?search=%s[img src=STATIC_URL/images/icons/pages.gif float=right][/pre]\r\n[/div]\r\n[script]\r\nsetTimeout(() => $WH.clickToCopy($WH.qs("pre > img"), "HOST_URL/?search=%s"), 100);\r\n[/script]\r\n[pad]\r\nSave your changes, and you\'ll be able to perform Aowow searches by typing "db" followed by the search terms in the address bar (e.g. db sword).\r\n[div clear=both][/div]\r\n' WHERE `url` = 'searchplugins' AND `locale` = 0;
diff --git a/setup/sql/updates/1753572319_01.sql b/setup/sql/updates/1753572319_01.sql
new file mode 100644
index 00000000..2e45b083
--- /dev/null
+++ b/setup/sql/updates/1753572319_01.sql
@@ -0,0 +1,14 @@
+ALTER TABLE `aowow_account`
+ DROP INDEX `user`,
+ CHANGE COLUMN `user` `login` varchar(64) NOT NULL DEFAULT '' COMMENT 'only used for login',
+ CHANGE COLUMN `displayName` `username` varchar(64) NOT NULL COMMENT 'unique; used for for links and display',
+ MODIFY COLUMN `email` varchar(64) DEFAULT NULL COMMENT 'unique; can be used for login if AUTH_SELF and can be NULL if not',
+ MODIFY COLUMN `token` varchar(40) DEFAULT NULL COMMENT 'identification key for changes to account',
+ ADD COLUMN `updateValue` varchar(128) DEFAULT NULL COMMENT 'temp store for new passHash / email' AFTER `token`,
+ ADD CONSTRAINT `username` UNIQUE (`username`);
+
+UPDATE `aowow_account`
+ SET `email` = NULL WHERE `email` = '';
+
+ALTER TABLE `aowow_account`
+ ADD CONSTRAINT `email` UNIQUE (`email`);
diff --git a/setup/sql/updates/1753574969_01.sql b/setup/sql/updates/1753574969_01.sql
new file mode 100644
index 00000000..4d599283
--- /dev/null
+++ b/setup/sql/updates/1753574969_01.sql
@@ -0,0 +1,17 @@
+DROP TABLE IF EXISTS `aowow_account_sessions`;
+CREATE TABLE `aowow_account_sessions` (
+ `userId` int unsigned NOT NULL,
+ `sessionId` varchar(190) NOT NULL COMMENT 'PHPSESSID', -- max size (for utf8mb4) to still be a key
+ `created` int unsigned NOT NULL,
+ `expires` int unsigned NOT NULL COMMENT 'timestamp or 0 (never expires)',
+ `touched` int unsigned NOT NULL COMMENT 'timestamp - last used',
+ `deviceInfo` varchar(256) NOT NULL,
+ `ip` varchar(45) NOT NULL COMMENT 'can change; just last used ip', -- think mobile switching between WLAN and mobile data
+ `status` enum('ACTIVE', 'LOGOUT', 'FORCEDLOGOUT', 'EXPIRED') NOT NULL,
+ UNIQUE KEY `sessionId` (`sessionId`) USING BTREE,
+ KEY `userId` (`userId`) USING BTREE,
+ CONSTRAINT `FK_acc_sessions` FOREIGN KEY (`userId`) REFERENCES `aowow_account` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=COMPACT;
+
+ALTER TABLE `aowow_account`
+ DROP COLUMN `allowExpire`;
diff --git a/setup/sql/updates/1753635510_01.sql b/setup/sql/updates/1753635510_01.sql
new file mode 100644
index 00000000..77f23fbe
--- /dev/null
+++ b/setup/sql/updates/1753635510_01.sql
@@ -0,0 +1,160 @@
+ALTER TABLE `aowow_articles`
+ DROP COLUMN `quickInfo`;
+
+DROP TABLE IF EXISTS `aowow_quickfacts`;
+CREATE TABLE `aowow_quickfacts` (
+ `type` smallint unsigned NOT NULL,
+ `typeId` mediumint signed NOT NULL,
+ `orderIdx` tinyint signed NOT NULL COMMENT '<0: prepend to generic list; >0: append to generic list',
+ `row` varchar(200) NOT NULL COMMENT 'Markdown formated',
+ UNIQUE KEY `row` (`type`, `typeId`, `orderIdx`) USING BTREE
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=COMPACT;
+
+INSERT INTO `aowow_quickfacts` VALUES
+ -- Dungeons
+ (7, 206, 1, '|L:zone:boss|[icon preset=boss][npc=23954][/icon]'),
+ (7, 209, 1, '|L:zone:boss|[icon preset=boss][npc=4275][/icon]'),
+ (7, 491, 1, '|L:zone:boss|[icon preset=boss][npc=4421][/icon]'),
+ (7, 717, 1, '|L:zone:boss|[icon preset=boss][npc=1716][/icon]'),
+ (7, 718, 1, '|L:zone:boss|[icon preset=boss][npc=5775][/icon]'),
+ (7, 719, 1, '|L:zone:boss|[icon preset=boss][npc=4829][/icon]'),
+ (7, 721, 1, '|L:zone:boss|[icon preset=boss][npc=7800][/icon]'),
+ (7, 722, 1, '|L:zone:boss|[icon preset=boss][npc=7358][/icon]'),
+ (7, 796, 1, '|L:zone:key:0|[item=7146]'),
+ (7, 796, 2, '|L:zone:boss|[icon preset=boss][npc=3976][/icon]'),
+ (7, 1176, 1, '|L:zone:boss|[icon preset=boss][npc=7267][/icon]'),
+ (7, 1196, 1, '|L:zone:boss|[icon preset=boss][npc=26861][/icon]'),
+ (7, 1337, 1, '|L:zone:boss|[icon preset=boss][npc=2748][/icon]'),
+ (7, 1477, 1, '|L:zone:boss|[icon preset=boss][npc=5709][/icon]'),
+ (7, 1581, 1, '|L:zone:boss|[icon preset=boss][npc=639][/icon]'),
+ (7, 1583, 1, '|L:zone:boss|[icon preset=boss][npc=10363][/icon]'),
+ (7, 1584, 1, '|L:zone:boss|[icon preset=boss][npc=9019][/icon]'),
+ (7, 2017, 1, '|L:zone:boss|[icon preset=boss][npc=10440][/icon]'),
+ (7, 2057, 1, '|L:zone:key:0|[item=13704]'),
+ (7, 2057, 2, '|L:zone:boss|[icon preset=boss][npc=1853][/icon]'),
+ (7, 2100, 1, '|L:zone:boss|[icon preset=boss][npc=12201][/icon]'),
+ (7, 2366, 1, '|L:zone:faction|[faction=989]'),
+ (7, 2366, 2, '|L:zone:boss|[icon preset=boss][npc=17881][/icon]'),
+ (7, 2367, 1, '|L:zone:faction|[faction=989]'),
+ (7, 2367, 2, '|L:zone:boss|[icon preset=boss][npc=18096][/icon]'),
+ (7, 2437, 1, '|L:zone:boss|[icon preset=boss][npc=11520][/icon]'),
+ (7, 3562, 1, '|L:zone:faction|[icon name=side_alliance][faction=946][/icon] / [icon name=side_horde][faction=947][/icon]'),
+ (7, 3562, 2, '|L:zone:boss|[icon preset=boss][npc=17536][/icon]'),
+ (7, 3713, 1, '|L:zone:faction|[icon name=side_alliance][faction=946][/icon] / [icon name=side_horde][faction=947][/icon]'),
+ (7, 3713, 2, '|L:zone:boss|[icon preset=boss][npc=17377][/icon]'),
+ (7, 3714, 1, '|L:zone:key:0|[item=28395]'),
+ (7, 3714, 2, '|L:zone:faction|[icon name=side_alliance][faction=946][/icon] / [icon name=side_horde][faction=947][/icon]'),
+ (7, 3714, 3, '|L:zone:boss|[icon preset=boss][npc=16808][/icon]'),
+ (7, 3715, 1, '|L:zone:faction|[faction=942]'),
+ (7, 3715, 2, '|L:zone:boss|[icon preset=boss][npc=17798][/icon]'),
+ (7, 3716, 1, '|L:zone:faction|[faction=942]'),
+ (7, 3716, 2, '|L:zone:boss|[icon preset=boss][npc=17882][/icon]'),
+ (7, 3717, 1, '|L:zone:faction|[faction=942]'),
+ (7, 3717, 2, '|L:zone:boss|[icon preset=boss][npc=17942][/icon]'),
+ (7, 3789, 1, '|L:zone:key:0|[item=27991]'),
+ (7, 3789, 2, '|L:zone:faction|[faction=1011]'),
+ (7, 3789, 3, '|L:zone:boss|[icon preset=boss][npc=18708][/icon]'),
+ (7, 3790, 1, '|L:zone:faction|[faction=1011]'),
+ (7, 3790, 2, '|L:zone:boss|[icon preset=boss][npc=18373][/icon]'),
+ (7, 3791, 1, '|L:zone:faction|[faction=1011]'),
+ (7, 3791, 2, '|L:zone:boss|[icon preset=boss][npc=18473][/icon]'),
+ (7, 3792, 1, '|L:zone:faction|[faction=933]'),
+ (7, 3792, 2, '|L:zone:boss|[icon preset=boss][npc=18344][/icon]'),
+ (7, 3847, 1, '|L:zone:faction|[faction=935]'),
+ (7, 3847, 2, '|L:zone:boss|[icon preset=boss][npc=17977][/icon]'),
+ (7, 3848, 1, '|L:zone:key:0|[item=31084]'),
+ (7, 3848, 2, '|L:zone:faction|[faction=935]'),
+ (7, 3848, 3, '|L:zone:boss|[icon preset=boss][npc=20912][/icon]'),
+ (7, 3849, 1, '|L:zone:faction|[faction=935]'),
+ (7, 3849, 2, '|L:zone:boss|[icon preset=boss][npc=19220][/icon]'),
+ (7, 4100, 1, '|L:zone:boss|[icon preset=boss][npc=26533][/icon]'),
+ (7, 4131, 1, '|L:zone:faction|[faction=1077]'),
+ (7, 4131, 2, '|L:zone:boss|[icon preset=boss][npc=24664][/icon]'),
+ (7, 4196, 1, '|L:zone:boss|[icon preset=boss][npc=26632][/icon]'),
+ (7, 4228, 1, '|L:zone:boss|[icon preset=boss][npc=27656][/icon]'),
+ (7, 4264, 1, '|L:zone:boss|[icon preset=boss][npc=27978][/icon]'),
+ (7, 4265, 1, '|L:zone:boss|[icon preset=boss][npc=26723][/icon]'),
+ (7, 4272, 1, '|L:zone:boss|[icon preset=boss][npc=28923][/icon]'),
+ (7, 4277, 1, '|L:zone:boss|[icon preset=boss][npc=29120][/icon]'),
+ (7, 4415, 1, '|L:zone:boss|[icon preset=boss][npc=31134][/icon]'),
+ (7, 4416, 1, '|L:zone:boss|[icon preset=boss][npc=29306][/icon]'),
+ (7, 4494, 1, '|L:zone:boss|[icon preset=boss][npc=29311][/icon]'),
+ (7, 4723, 1, '|L:zone:boss|[icon preset=boss][npc=35451][/icon]'),
+ (7, 4809, 1, '|L:zone:boss|[icon preset=boss][npc=36502][/icon]'),
+ (7, 4813, 1, '|L:zone:boss|[icon preset=boss][npc=36658][/icon]'),
+ (7, 4820, 1, '|L:zone:boss|[icon preset=boss][npc=36954][/icon]'),
+ -- Raids
+ (7, 1977, 1, '|L:zone:raidFaction|[faction=270]'),
+ (7, 1977, 2, '|L:zone:boss|[icon preset=boss][npc=14834][/icon]'),
+ (7, 2677, 1, '|L:zone:attunement:0|[quest=7761]'),
+ (7, 2677, 2, '|L:zone:boss|[icon preset=boss][npc=11583][/icon]'),
+ (7, 2717, 1, '|L:zone:attunement:0|[quest=7487]'),
+ (7, 2717, 2, '|L:zone:raidFaction|[faction=749]'),
+ (7, 2717, 3, '|L:zone:boss|[icon preset=boss][npc=11502][/icon]'),
+ (7, 3428, 1, '|L:zone:raidFaction|[faction=910]'),
+ (7, 3428, 2, '|L:zone:boss|[icon preset=boss][npc=15727][/icon]'),
+ (7, 3429, 1, '|L:zone:raidFaction|[faction=609]'),
+ (7, 3429, 2, '|L:zone:boss|[icon preset=boss][npc=15339][/icon]'),
+ (7, 3457, 1, '|L:zone:attunement:0|[quest=9837]'),
+ (7, 3457, 2, '|L:zone:key:0|[item=24490]'),
+ (7, 3457, 3, '|L:zone:raidFaction|[faction=967]'),
+ (7, 3457, 4, '|L:zone:boss|[icon preset=boss][npc=15690][/icon]'),
+ (7, 3606, 1, '|L:zone:raidFaction|[faction=990]'),
+ (7, 3606, 2, '|L:zone:boss|[icon preset=boss][npc=17968][/icon]'),
+ (7, 3607, 1, '|L:zone:boss|[icon preset=boss][npc=21212][/icon]'),
+ (7, 3805, 1, '|L:zone:boss|[icon preset=boss][npc=23863][/icon]'),
+ (7, 3836, 1, '|L:zone:boss|[icon preset=boss][npc=17257][/icon]'),
+ (7, 3845, 1, '|L:zone:boss|[icon preset=boss][npc=19622][/icon]'),
+ (7, 3923, 1, '|L:zone:boss|[icon preset=boss][npc=19044][/icon]'),
+ (7, 3959, 1, '|L:zone:raidFaction|[faction=1012]'),
+ (7, 3959, 2, '|L:zone:boss|[icon preset=boss][npc=22917][/icon]'),
+ (7, 4075, 1, '|L:zone:boss|[icon preset=boss][npc=25315][/icon]'),
+ -- Zones
+ (7, 1, 1, '|L:zone:city||L:main:colon|[zone=1537]'),
+ (7, 12, 1, '|L:zone:city||L:main:colon|[zone=1519]'),
+ (7, 14, 1, '|L:zone:city||L:main:colon|[zone=1637]'),
+ (7, 65, 1, '|L:zone:reputationHub|[faction=1091]'),
+ (7, 67, 1, '|L:zone:reputationHub|[faction=1119]'),
+ (7, 85, 1, '|L:zone:city||L:main:colon|[zone=1497]'),
+ (7, 139, 1, '|L:zone:reputationHub|[faction=529]'),
+ (7, 141, 1, '|L:zone:city||L:main:colon|[zone=1657]'),
+ (7, 210, 1, '|L:zone:reputationHub|[faction=1106]\n[faction=1098]'),
+ (7, 215, 1, '|L:zone:city||L:main:colon|[zone=1638]'),
+ (7, 361, 1, '|L:zone:reputationHub|[faction=576]'),
+ (7, 405, 1, '|L:zone:reputationHub|[faction=92]\n[faction=93]'),
+ (7, 440, 1, '|L:zone:reputationHub|[faction=989]'),
+ (7, 493, 1, '|L:game:class||L:main:colon|[class=11]'),
+ (7, 618, 1, '|L:zone:reputationHub|[faction=589]'),
+ (7, 1377, 1, '|L:zone:reputationHub|[faction=609]'),
+ (7, 1497, 1, '|L:zone:location|[zone=85]'),
+ (7, 1497, 2, '|L:zone:reputationHub|[faction=68]'),
+ (7, 1519, 1, '|L:zone:location|[zone=12]'),
+ (7, 1519, 2, '|L:zone:reputationHub|[faction=72]'),
+ (7, 1537, 1, '|L:zone:location|[zone=1]'),
+ (7, 1537, 2, '|L:zone:reputationHub|[faction=47]\n[faction=54]'),
+ (7, 1637, 1, '|L:zone:location|[zone=14]'),
+ (7, 1637, 2, '|L:zone:reputationHub|[faction=76]'),
+ (7, 1638, 1, '|L:zone:location|[zone=215]'),
+ (7, 1638, 2, '|L:zone:reputationHub|[faction=81]'),
+ (7, 1657, 1, '|L:zone:location|[zone=141]'),
+ (7, 1657, 2, '|L:zone:reputationHub|[faction=69]'),
+ (7, 3430, 1, '|L:zone:city||L:main:colon|[zone=3487]'),
+ (7, 3433, 1, '|L:zone:reputationHub|[faction=922]'),
+ (7, 3483, 1, '|L:zone:reputationHub|[icon name=side_alliance][faction=946][/icon]\n[icon name=side_horde][faction=947][/icon]'),
+ (7, 3487, 1, '|L:zone:location|[zone=3430]'),
+ (7, 3487, 2, '|L:zone:reputationHub|[faction=911]'),
+ (7, 3518, 1, '|L:zone:reputationHub|[icon name=side_alliance][faction=978][/icon]\n[icon name=side_horde][faction=941][/icon]'),
+ (7, 3519, 1, '|L:zone:reputationHub|[faction=1031]'),
+ (7, 3519, 2, '|L:zone:city||L:main:colon|[zone=3703]'),
+ (7, 3520, 1, '|L:zone:reputationHub|[faction=1015]'),
+ (7, 3521, 1, '|L:zone:reputationHub|[faction=942]\n[faction=970]'),
+ (7, 3522, 1, '|L:zone:reputationHub|[faction=1038]'),
+ (7, 3523, 1, '|L:zone:reputationHub|[faction=933]'),
+ (7, 3557, 1, '|L:zone:location|[zone=3524]'),
+ (7, 3557, 2, '|L:zone:reputationHub|[faction=930]'),
+ (7, 3711, 1, '|L:zone:reputationHub|[faction=1105]\n[faction=1104]'),
+ (7, 3703, 1, '|L:zone:location|[zone=3519]'),
+ (7, 3703, 2, '|L:zone:reputationHub|[faction=932]\n[faction=934]\n[faction=1011]'),
+ (7, 4080, 1, '|L:zone:reputationHub|[faction=1077]'),
+ (7, 4395, 1, '|L:zone:location|[zone=2817]'),
+ (7, 4395, 2, '|L:zone:reputationHub|[faction=1090]');
diff --git a/setup/sql/updates/1753977720_01.sql b/setup/sql/updates/1753977720_01.sql
new file mode 100644
index 00000000..bef0e1a9
--- /dev/null
+++ b/setup/sql/updates/1753977720_01.sql
@@ -0,0 +1 @@
+UPDATE `aowow_dbversion` SET `sql` = CONCAT(IFNULL(`sql`, ''), ' source');
diff --git a/setup/sql/updates/1758578400_01.sql b/setup/sql/updates/1758578400_01.sql
new file mode 100644
index 00000000..66e1f52b
--- /dev/null
+++ b/setup/sql/updates/1758578400_01.sql
@@ -0,0 +1,6 @@
+DELETE FROM `aowow_config` WHERE `key` IN ('rep_req_ext_links', 'gtag_measurement_id');
+INSERT INTO `aowow_config` (`key`, `value`, `default`, `cat`, `flags`, `comment`) VALUES
+ ('rep_req_ext_links', 150, 150, 5, 129, 'required reputation to link to external sites'),
+ ('gtag_measurement_id', '', NULL, 6, 136, 'Enter your Google Tag measurement ID here to track site stats');
+
+UPDATE `aowow_config` SET `key` = 'ua_measurement_key', `comment` = '[DEPRECATED ?] Enter your Google Universal Analytics key here to track site stats' WHERE `key` = 'analytics_user';
diff --git a/setup/sql/updates/1758578400_02.sql b/setup/sql/updates/1758578400_02.sql
new file mode 100644
index 00000000..ca98a880
--- /dev/null
+++ b/setup/sql/updates/1758578400_02.sql
@@ -0,0 +1 @@
+UPDATE `aowow_articles` SET `url` = CONCAT('help=', `url`) WHERE `url` IN ('commenting-and-you', 'item-comparison', 'modelviewer', 'profiler', 'screenshots-tips-tricks', 'stat-weighting', 'talent-calculator', 'markup-guide');
diff --git a/setup/sql/updates/1758578400_03.sql b/setup/sql/updates/1758578400_03.sql
new file mode 100644
index 00000000..a579869f
--- /dev/null
+++ b/setup/sql/updates/1758578400_03.sql
@@ -0,0 +1,2 @@
+UPDATE `aowow_config` SET `key` = 'rep_req_border_legendary' WHERE `key` = 'rep_req_border_lege';
+UPDATE `aowow_config` SET `key` = 'rep_req_border_uncommon' WHERE `key` = 'rep_req_border_unco';
diff --git a/setup/sql/updates/1758578400_04.sql b/setup/sql/updates/1758578400_04.sql
new file mode 100644
index 00000000..7d6f7c5b
--- /dev/null
+++ b/setup/sql/updates/1758578400_04.sql
@@ -0,0 +1,17 @@
+ALTER TABLE `aowow_account`
+ CHANGE COLUMN `avatar` `wowicon` varchar(55) NOT NULL DEFAULT '' COMMENT 'iconname as avatar',
+ ADD COLUMN `avatar` tinyint(1) NOT NULL DEFAULT 0 COMMENT 'selected avatar mode' AFTER `userGroups`;
+
+DROP TABLE IF EXISTS `aowow_account_avatars`;
+CREATE TABLE `aowow_account_avatars` (
+ `id` mediumint unsigned NOT NULL,
+ `userId` int unsigned NOT NULL,
+ `name` varchar(20) NOT NULL,
+ `size` mediumint unsigned NOT NULL,
+ `when` int unsigned NOT NULL,
+ `current` tinyint unsigned NOT NULL DEFAULT 0,
+ `status` tinyint unsigned NOT NULL DEFAULT 0,
+ UNIQUE KEY `id` (`id`) USING BTREE,
+ KEY `userId` (`userId`) USING BTREE,
+ CONSTRAINT `FK_acc_avatars` FOREIGN KEY (`userId`) REFERENCES `aowow_account` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=COMPACT;
diff --git a/setup/sql/updates/1758578400_05.sql b/setup/sql/updates/1758578400_05.sql
new file mode 100644
index 00000000..d36973d1
--- /dev/null
+++ b/setup/sql/updates/1758578400_05.sql
@@ -0,0 +1,3 @@
+DELETE FROM `aowow_config` WHERE `key` = 'screenshot_min_size';
+INSERT INTO `aowow_config` (`key`, `value`, `default`, `cat`, `flags`, `comment`) VALUES
+ ('screenshot_min_size', 200, 200, 1, 1153, "minimum dimensions of uploaded screenshots in px (yes, it's square, no it cant go below 200)");
diff --git a/setup/sql/updates/1758578400_06.sql b/setup/sql/updates/1758578400_06.sql
new file mode 100644
index 00000000..9a8c8a9d
--- /dev/null
+++ b/setup/sql/updates/1758578400_06.sql
@@ -0,0 +1,2 @@
+ALTER TABLE `aowow_screenshots`
+ MODIFY COLUMN `caption` varchar(200) DEFAULT NULL;
diff --git a/setup/sql/updates/1758578400_07.sql b/setup/sql/updates/1758578400_07.sql
new file mode 100644
index 00000000..4ba7523f
--- /dev/null
+++ b/setup/sql/updates/1758578400_07.sql
@@ -0,0 +1,8 @@
+-- `key` is too small for our new configs
+ALTER TABLE `aowow_config`
+ MODIFY COLUMN `key` varchar(50) NOT NULL;
+
+-- split generic upload in ss / vi
+UPDATE `aowow_config` SET `key` = 'rep_reward_submit_screenshot', `comment` = 'uploaded screenshot was approved' WHERE `key` = 'rep_reward_upload';
+DELETE FROM `aowow_config` WHERE `key` = 'rep_reward_suggest_video';
+INSERT INTO `aowow_config` VALUES ('rep_reward_suggest_video', '10', '10', 5, 129, 'suggested video was approved');
diff --git a/setup/sql/updates/1758578400_08.sql b/setup/sql/updates/1758578400_08.sql
new file mode 100644
index 00000000..f7ad1861
--- /dev/null
+++ b/setup/sql/updates/1758578400_08.sql
@@ -0,0 +1,8 @@
+-- update video storage
+ALTER TABLE `aowow_videos`
+ ADD COLUMN `pos` tinyint unsigned NOT NULL AFTER `videoId`,
+ ADD COLUMN `url` varchar(64) NOT NULL COMMENT 'preview thumb' AFTER `pos`,
+ ADD COLUMN `width` smallint unsigned NOT NULL AFTER `url`,
+ ADD COLUMN `height` smallint unsigned NOT NULL AFTER `width`,
+ ADD COLUMN `name` varchar(64) DEFAULT NULL AFTER `height`,
+ MODIFY COLUMN `caption` varchar(200) DEFAULT NULL;
diff --git a/setup/sql/updates/1758578400_09.sql b/setup/sql/updates/1758578400_09.sql
new file mode 100644
index 00000000..3e2ddcbb
--- /dev/null
+++ b/setup/sql/updates/1758578400_09.sql
@@ -0,0 +1,4 @@
+-- update article affected by cfg change
+UPDATE `aowow_articles` SET
+ `article` = '[b]Reputation[/b] is a rough measurement of how much you participate in the community--it is earned by convincing your peers that you know what you’re talking about. Our community puts just as much work as our developers do into making our site as awesome as it is and reputation is meant as a way for you to track just how much work you\'re putting into us.\r\n\r\nThe primary means of gaining reputation is by posting quality comments on database entries (which are then voted up by other site members) and by general contributions to the site which can include actions like data and screenshot submissions. Whenever you leave a comment on a database entry, your peers can then vote on these comments, and those votes will cause you to gain reputation. You can also earn reputation by voting on other users\' comments and by sending in reports!\r\n\r\nBy being a good-standing and contributing user you will be able to earn both reputation and achievements for many of the same actions!\r\n\r\n[h3]Reputation Gains[/h3]\r\n[div style=\"max-width:400px\"][table class=grid]\r\n[tr][td][url=?account=signup]Registering[/url] an account[/td]\r\n[td align=right class=no-wrap]CFG_REP_REWARD_REGISTER reputation[/td]\r\n[/tr]\r\n[tr][td]Daily visit[/td]\r\n[td align=right class=no-wrap]CFG_REP_REWARD_DAILYVISIT reputation[/td]\r\n[/tr]\r\n[tr][td]Posting a comment[/td]\r\n[td align=right class=no-wrap]CFG_REP_REWARD_COMMENT reputation[/td]\r\n[/tr]\r\n[tr][td]Your comment was voted up (each upvote)[/td]\r\n[td align=right class=no-wrap]CFG_REP_REWARD_UPVOTED reputation[/td]\r\n[/tr]\r\n[tr][td]Submitting a screenshot[/td]\r\n[td align=right class=no-wrap]CFG_REP_REWARD_SUBMIT_SCREENSHOT reputation[/td]\r\n[/tr]\r\n[tr][td]Suggesting a video[/td]\r\n[td align=right class=no-wrap]CFG_REP_REWARD_SUGGEST_VIDEO reputation[/td]\r\n[/tr]\r\n[tr][td]Submitting a guide (approved)[/td]\r\n[td align=right class=no-wrap]CFG_REP_REWARD_ARTICLE reputation[/td]\r\n[/tr]\r\n[tr][td]Filing a report (accepted)[/td]\r\n[td align=right class=no-wrap]CFG_REP_REWARD_GOOD_REPORT reputation[/td]\r\n[/tr]\r\n[/table][/div]\r\n\r\n\r\n[h3]Site Privileges[/h3]\r\nThe higher your reputation level, the more privileges you gain. Earn a high enough reputation to unlock additional rewards, in the form of new privileges around the site!\r\n[pad]\r\n[div style=\"max-width:400px\"][table class=grid]\r\n[tr][td]Post comments[/td]\r\n[td align=right class=no-wrap]CFG_REP_REQ_COMMENT reputation[/td]\r\n[/tr]\r\n[tr][td]Upvote on comments[/td]\r\n[td align=right class=no-wrap]CFG_REP_REQ_UPVOTE reputation[/td]\r\n[/tr]\r\n[tr][td]Downvote on comments[/td]\r\n[td align=right class=no-wrap]CFG_REP_REQ_DOWNVOTE reputation[/td]\r\n[/tr]\r\n[tr][td]More votes per day[/td]\r\n[td align=right class=no-wrap]CFG_REP_REQ_VOTEMORE_BASE reputation[/td]\r\n[/tr]\r\n[tr][td]Comment votes worth more[/td]\r\n[td align=right class=no-wrap]CFG_REP_REQ_SUPERVOTE reputation[/td]\r\n[/tr]\r\n[/table][/div]\r\n[pad]\r\n[url=?privileges]Check out full details on site privileges you can earn![/url]\r\n'
+WHERE `url` = 'reputation' AND `locale` = 0;
diff --git a/setup/sql/updates/1758578400_10.sql b/setup/sql/updates/1758578400_10.sql
new file mode 100644
index 00000000..5e7cd68d
--- /dev/null
+++ b/setup/sql/updates/1758578400_10.sql
@@ -0,0 +1,2 @@
+-- set on_set_fn check
+UPDATE `aowow_config` SET `flags` = `flags` | 1024 WHERE `key` = 'cache_mode';
diff --git a/setup/sql/updates/1758578400_11.sql b/setup/sql/updates/1758578400_11.sql
new file mode 100644
index 00000000..e525afa0
--- /dev/null
+++ b/setup/sql/updates/1758578400_11.sql
@@ -0,0 +1,3 @@
+ALTER TABLE `aowow_account`
+ ADD COLUMN `debug` tinyint(1) NOT NULL DEFAULT 0 COMMENT 'show ids in lists user option' AFTER `userGroups`,
+ MODIFY COLUMN `description` text NOT NULL DEFAULT '';
diff --git a/setup/sql/updates/1758578400_12.sql b/setup/sql/updates/1758578400_12.sql
new file mode 100644
index 00000000..ad48339e
--- /dev/null
+++ b/setup/sql/updates/1758578400_12.sql
@@ -0,0 +1,11 @@
+ALTER TABLE `aowow_user_ratings`
+ DROP KEY `FK_acc_co_rate_user`,
+ DROP FOREIGN KEY `FK_userId`,
+ DROP PRIMARY KEY;
+
+ALTER TABLE `aowow_user_ratings` MODIFY `userId` int unsigned NULL;
+
+ALTER TABLE `aowow_user_ratings`
+ ADD UNIQUE KEY (`type`,`entry`,`userId`),
+ ADD KEY `FK_acc_co_rate_user` (`userId`),
+ ADD CONSTRAINT FK_userId FOREIGN KEY (`userId`) REFERENCES aowow_account(`id`) ON DELETE SET NULL ON UPDATE CASCADE;
diff --git a/setup/sql/updates/1758578400_13.sql b/setup/sql/updates/1758578400_13.sql
new file mode 100644
index 00000000..c055f944
--- /dev/null
+++ b/setup/sql/updates/1758578400_13.sql
@@ -0,0 +1,2 @@
+ALTER TABLE `aowow_account`
+ ADD COLUMN `renameCooldown` int unsigned NOT NULL DEFAULT 0 COMMENT 'timestamp when rename is available again' AFTER `updateValue`;
diff --git a/setup/sql/updates/1758578400_14.sql b/setup/sql/updates/1758578400_14.sql
new file mode 100644
index 00000000..3445704e
--- /dev/null
+++ b/setup/sql/updates/1758578400_14.sql
@@ -0,0 +1,2 @@
+DELETE FROM `aowow_config` WHERE `key` = 'acc_rename_decay';
+INSERT INTO `aowow_config` VALUES ('acc_rename_decay', 30 * 24 * 60 * 60, '30 * 24 * 60 * 60', 3, 129, 'delay between username changes');
diff --git a/setup/sql/updates/1758578400_15.sql b/setup/sql/updates/1758578400_15.sql
new file mode 100644
index 00000000..32ee0455
--- /dev/null
+++ b/setup/sql/updates/1758578400_15.sql
@@ -0,0 +1,3 @@
+DELETE FROM `aowow_config` WHERE `key` = 'acc_max_avatar_uploads';
+INSERT INTO `aowow_config` (`key`, `value`, `default`, `cat`, `flags`, `comment`) VALUES
+ ('acc_max_avatar_uploads', 10, 10, 3, 129, 'premium users may upload this many avatars');
diff --git a/setup/sql/updates/1758578400_16.sql b/setup/sql/updates/1758578400_16.sql
new file mode 100644
index 00000000..4b2f0d43
--- /dev/null
+++ b/setup/sql/updates/1758578400_16.sql
@@ -0,0 +1,2 @@
+ALTER TABLE `aowow_account`
+ ADD COLUMN `avatarborder` tinyint unsigned NOT NULL DEFAULT 2 AFTER `avatar`;
diff --git a/setup/sql/updates/1758578400_17.sql b/setup/sql/updates/1758578400_17.sql
new file mode 100644
index 00000000..03adf418
--- /dev/null
+++ b/setup/sql/updates/1758578400_17.sql
@@ -0,0 +1 @@
+UPDATE `aowow_dbversion` SET `build` = CONCAT(IFNULL(`build`, ''), ' globaljs');
diff --git a/setup/sql/updates/1759504522_01.sql b/setup/sql/updates/1759504522_01.sql
new file mode 100644
index 00000000..12872d0e
--- /dev/null
+++ b/setup/sql/updates/1759504522_01.sql
@@ -0,0 +1,11 @@
+ALTER TABLE aowow_profiler_completion_quests
+ ADD KEY `typeId` (`questId`);
+
+ALTER TABLE aowow_profiler_completion_reputation
+ ADD KEY `typeId` (`factionId`);
+
+ALTER TABLE aowow_profiler_completion_spells
+ ADD KEY `typeId` (`spellId`);
+
+ALTER TABLE aowow_profiler_completion_titles
+ ADD KEY `typeId` (`titleId`);
diff --git a/setup/sql/updates/1759504522_02.sql b/setup/sql/updates/1759504522_02.sql
new file mode 100644
index 00000000..03adf418
--- /dev/null
+++ b/setup/sql/updates/1759504522_02.sql
@@ -0,0 +1 @@
+UPDATE `aowow_dbversion` SET `build` = CONCAT(IFNULL(`build`, ''), ' globaljs');
diff --git a/setup/sql/updates/1760300362_01.sql b/setup/sql/updates/1760300362_01.sql
new file mode 100644
index 00000000..bef0e1a9
--- /dev/null
+++ b/setup/sql/updates/1760300362_01.sql
@@ -0,0 +1 @@
+UPDATE `aowow_dbversion` SET `sql` = CONCAT(IFNULL(`sql`, ''), ' source');
diff --git a/setup/sql/updates/1760557948_01.sql b/setup/sql/updates/1760557948_01.sql
new file mode 100644
index 00000000..03adf418
--- /dev/null
+++ b/setup/sql/updates/1760557948_01.sql
@@ -0,0 +1 @@
+UPDATE `aowow_dbversion` SET `build` = CONCAT(IFNULL(`build`, ''), ' globaljs');
diff --git a/setup/sql/updates/1760911493_01.sql b/setup/sql/updates/1760911493_01.sql
new file mode 100644
index 00000000..b4f362f0
--- /dev/null
+++ b/setup/sql/updates/1760911493_01.sql
@@ -0,0 +1,7 @@
+ALTER TABLE `aowow_profiler_pets`
+ MODIFY COLUMN `talents` varchar(22) DEFAULT NULL;
+
+UPDATE `aowow_dbversion` SET `build` = CONCAT(IFNULL(`build`, ''), ' talenticons talentcalc');
+
+-- flag all hunters as requiring update
+UPDATE `aowow_profiler_profiles` SET `flags` = `flags` | 16, `lastupdated` = 0 WHERE `class` = 3 AND `realmGUID` IS NOT NULL;
diff --git a/setup/sql/updates/1760979519_01.sql b/setup/sql/updates/1760979519_01.sql
new file mode 100644
index 00000000..c0c38b25
--- /dev/null
+++ b/setup/sql/updates/1760979519_01.sql
@@ -0,0 +1 @@
+UPDATE `aowow_dbversion` SET `sql` = CONCAT(IFNULL(`sql`, ''), ' spawns');
diff --git a/setup/sql/updates/1760979719_01.sql b/setup/sql/updates/1760979719_01.sql
new file mode 100644
index 00000000..c2bbcf7a
--- /dev/null
+++ b/setup/sql/updates/1760979719_01.sql
@@ -0,0 +1,8 @@
+ALTER TABLE `aowow_factions`
+ DROP COLUMN `baseRepValue3`,
+ DROP COLUMN `baseRepValue4`,
+ ADD COLUMN `baseRepValue3` mediumint(9) NOT NULL AFTER `baseRepValue2`,
+ ADD COLUMN `baseRepValue4` mediumint(9) NOT NULL AFTER `baseRepValue3`
+;
+
+UPDATE `aowow_dbversion` SET `sql` = CONCAT(IFNULL(`sql`, ''), ' factions');
diff --git a/setup/sql/updates/1761145594_01.sql b/setup/sql/updates/1761145594_01.sql
new file mode 100644
index 00000000..03adf418
--- /dev/null
+++ b/setup/sql/updates/1761145594_01.sql
@@ -0,0 +1 @@
+UPDATE `aowow_dbversion` SET `build` = CONCAT(IFNULL(`build`, ''), ' globaljs');
diff --git a/setup/sql/updates/1761148832_01.sql b/setup/sql/updates/1761148832_01.sql
new file mode 100644
index 00000000..03adf418
--- /dev/null
+++ b/setup/sql/updates/1761148832_01.sql
@@ -0,0 +1 @@
+UPDATE `aowow_dbversion` SET `build` = CONCAT(IFNULL(`build`, ''), ' globaljs');
diff --git a/setup/sql/updates/1762199391_01.sql b/setup/sql/updates/1762199391_01.sql
new file mode 100644
index 00000000..168a5f1b
--- /dev/null
+++ b/setup/sql/updates/1762199391_01.sql
@@ -0,0 +1 @@
+UPDATE `aowow_dbversion` SET `build` = CONCAT(IFNULL(`build`, ''), ' globaljs tooltips');
diff --git a/setup/sql/updates/1762352733_01.sql b/setup/sql/updates/1762352733_01.sql
new file mode 100644
index 00000000..203b6646
--- /dev/null
+++ b/setup/sql/updates/1762352733_01.sql
@@ -0,0 +1,3 @@
+ALTER TABLE `aowow_items`
+ ADD KEY spellId1 (`spellId1`),
+ ADD KEY spellId2 (`spellId2`);
diff --git a/setup/sql/updates/1762543652_01.sql b/setup/sql/updates/1762543652_01.sql
new file mode 100644
index 00000000..2e3027b3
--- /dev/null
+++ b/setup/sql/updates/1762543652_01.sql
@@ -0,0 +1,18 @@
+ALTER TABLE `aowow_spell`
+ ADD KEY reagent1 (`reagent1`),
+ ADD KEY reagent2 (`reagent2`),
+ ADD KEY reagent3 (`reagent3`),
+ ADD KEY reagent4 (`reagent4`),
+ ADD KEY reagent5 (`reagent5`),
+ ADD KEY reagent6 (`reagent6`),
+ ADD KEY reagent7 (`reagent7`),
+ ADD KEY reagent8 (`reagent8`),
+ ADD KEY effect1CreateItemId (`effect1CreateItemId`),
+ ADD KEY effect2CreateItemId (`effect2CreateItemId`),
+ ADD KEY effect3CreateItemId (`effect3CreateItemId`),
+ ADD KEY effect1Id (`effect1Id`),
+ ADD KEY effect2Id (`effect2Id`),
+ ADD KEY effect3Id (`effect3Id`),
+ ADD KEY effect1AuraId (`effect1AuraId`),
+ ADD KEY effect2AuraId (`effect2AuraId`),
+ ADD KEY effect3AuraId (`effect3AuraId`);
diff --git a/setup/sql/updates/1762629696_01.sql b/setup/sql/updates/1762629696_01.sql
new file mode 100644
index 00000000..40beca4d
--- /dev/null
+++ b/setup/sql/updates/1762629696_01.sql
@@ -0,0 +1,18 @@
+ALTER TABLE aowow_profiler_profiles
+ ADD COLUMN `custom` tinyint(1) DEFAULT 0 COMMENT 'custom profile' AFTER `cuFlags`,
+ ADD COLUMN `stub` tinyint(1) DEFAULT 0 COMMENT 'character stub needs resync' AFTER `custom`,
+ ADD COLUMN `deleted` tinyint(1) DEFAULT 0 COMMENT 'only on custom profiles' AFTER `stub`,
+ ADD KEY `idx_custom` (`custom`),
+ ADD KEY `idx_stub` (`stub`),
+ ADD KEY `idx_deleted` (`deleted`)
+;
+
+ALTER TABLE aowow_profiler_arena_team
+ ADD COLUMN `stub` tinyint(1) DEFAULT 0 COMMENT 'arena team stub needs resync' AFTER `cuFlags`,
+ ADD KEY `idx_stub` (`stub`)
+;
+
+ALTER TABLE aowow_profiler_guild
+ ADD COLUMN `stub` tinyint(1) DEFAULT 0 COMMENT 'guild stub needs resync' AFTER `cuFlags`,
+ ADD KEY `idx_stub` (`stub`)
+;
diff --git a/setup/sql/updates/1762629696_02.sql b/setup/sql/updates/1762629696_02.sql
new file mode 100644
index 00000000..c9f18965
--- /dev/null
+++ b/setup/sql/updates/1762629696_02.sql
@@ -0,0 +1,10 @@
+UPDATE aowow_profiler_profiles SET `deleted` = 1 WHERE `cuFlags` & 4;
+UPDATE aowow_profiler_profiles SET `custom` = 1 WHERE `cuFlags` & 8;
+UPDATE aowow_profiler_profiles SET `stub` = 1 WHERE `cuFlags` & 16;
+UPDATE aowow_profiler_profiles SET `cuFlags` = `cuFlags` & ~(4 | 8 | 16);
+
+UPDATE aowow_profiler_arena_team SET `stub` = 1 WHERE `cuFlags` & 16;
+UPDATE aowow_profiler_arena_team SET `cuFlags` = `cuFlags` & ~16;
+
+UPDATE aowow_profiler_guild SET `stub` = 1 WHERE `cuFlags` & 16;
+UPDATE aowow_profiler_guild SET `cuFlags` = `cuFlags` & ~16;
diff --git a/setup/sql/updates/1762700147_01.sql b/setup/sql/updates/1762700147_01.sql
new file mode 100644
index 00000000..dd011d10
--- /dev/null
+++ b/setup/sql/updates/1762700147_01.sql
@@ -0,0 +1,4 @@
+ALTER TABLE aowow_profiler_completion_reputation
+ ADD COLUMN `exalted` tinyint(1) GENERATED ALWAYS AS (`standing` >= 42000) STORED AFTER `standing`,
+ ADD KEY idx_exalted (`exalted`)
+;
diff --git a/setup/sql/updates/1763168697_01.sql b/setup/sql/updates/1763168697_01.sql
new file mode 100644
index 00000000..bef0e1a9
--- /dev/null
+++ b/setup/sql/updates/1763168697_01.sql
@@ -0,0 +1 @@
+UPDATE `aowow_dbversion` SET `sql` = CONCAT(IFNULL(`sql`, ''), ' source');
diff --git a/setup/sql/updates/1763200071_01.sql b/setup/sql/updates/1763200071_01.sql
new file mode 100644
index 00000000..27b16cdf
--- /dev/null
+++ b/setup/sql/updates/1763200071_01.sql
@@ -0,0 +1,22 @@
+UPDATE `aowow_pet` SET `expansion` = 1 WHERE `id` IN (30, 31, 32, 33, 34);
+UPDATE `aowow_pet` SET `expansion` = 2 WHERE `id` IN (37, 38, 39, 41, 42, 43, 44, 45, 46);
+
+DELETE FROM `aowow_setup_custom_data` WHERE `command` = 'pet' AND `field` = 'expansion';
+INSERT INTO `aowow_setup_custom_data` VALUES
+ ('pet', 30, 'expansion', 1, 'Pet - Dragonhawk: BC'),
+ ('pet', 31, 'expansion', 1, 'Pet - Ravager: BC'),
+ ('pet', 32, 'expansion', 1, 'Pet - Warp Stalker: BC'),
+ ('pet', 33, 'expansion', 1, 'Pet - Sporebat: BC'),
+ ('pet', 34, 'expansion', 1, 'Pet - Nether Ray: BC'),
+ ('pet', 37, 'expansion', 2, 'Pet - Moth: WotLK'),
+ ('pet', 38, 'expansion', 2, 'Pet - Chimaera: WotLK'),
+ ('pet', 39, 'expansion', 2, 'Pet - Devilsaur: WotLK'),
+ ('pet', 41, 'expansion', 2, 'Pet - Silithid: WotLK'),
+ ('pet', 42, 'expansion', 2, 'Pet - Worm: WotLK'),
+ ('pet', 43, 'expansion', 2, 'Pet - Rhino: WotLK'),
+ ('pet', 44, 'expansion', 2, 'Pet - Wasp: WotLK'),
+ ('pet', 45, 'expansion', 2, 'Pet - Core Hound: WotLK'),
+ ('pet', 46, 'expansion', 2, 'Pet - Spirit Beast: WotLK')
+;
+
+UPDATE `aowow_dbversion` SET `sql` = CONCAT(IFNULL(`sql`, ''), ' pet');
diff --git a/setup/sql/updates/1763240934_01.sql b/setup/sql/updates/1763240934_01.sql
new file mode 100644
index 00000000..c0c38b25
--- /dev/null
+++ b/setup/sql/updates/1763240934_01.sql
@@ -0,0 +1 @@
+UPDATE `aowow_dbversion` SET `sql` = CONCAT(IFNULL(`sql`, ''), ' spawns');
diff --git a/setup/sql/updates/1763555620_01.sql b/setup/sql/updates/1763555620_01.sql
new file mode 100644
index 00000000..03adf418
--- /dev/null
+++ b/setup/sql/updates/1763555620_01.sql
@@ -0,0 +1 @@
+UPDATE `aowow_dbversion` SET `build` = CONCAT(IFNULL(`build`, ''), ' globaljs');
diff --git a/setup/sql/updates/1763557620_01.sql b/setup/sql/updates/1763557620_01.sql
new file mode 100644
index 00000000..f7f6787e
--- /dev/null
+++ b/setup/sql/updates/1763557620_01.sql
@@ -0,0 +1,42 @@
+DROP TABLE IF EXISTS `aowow_objectdifficulty`;
+CREATE TABLE `aowow_objectdifficulty` (
+ `normal10` mediumint(8) unsigned NOT NULL,
+ `normal25` mediumint(8) unsigned NOT NULL,
+ `heroic10` mediumint(8) unsigned NOT NULL,
+ `heroic25` mediumint(8) unsigned NOT NULL,
+ `mapType` tinyint(3) unsigned NOT NULL,
+ KEY `normal10` (`normal10`),
+ KEY `normal25` (`normal25`),
+ KEY `heroic10` (`heroic10`),
+ KEY `heroic25` (`heroic25`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+
+INSERT INTO `aowow_objectdifficulty` VALUES
+ (181366, 193426, 0, 0 , 2), -- naxxramas: four horsemen chest
+ (193905, 193967, 0, 0 , 2), -- eoe: alexstrasza's gift
+ (194307, 194308, 194200, 194201, 2), -- ulduar: cache of winter
+ (194312, 194314, 194313, 194315, 2), -- ulduar: cache of storms
+ (194324, 194328, 194325, 194329, 2), -- ulduar: freya's gift +1 elder
+ (194324, 194328, 194326, 194330, 2), -- ulduar: freya's gift +2 elder
+ (194324, 194328, 194327, 194331, 2), -- ulduar: freya's gift +3 elder
+ (194789, 194956, 194957, 194958, 2), -- ulduar: cache of innovation
+ (194821, 194822, 0, 0 , 2), -- ulduar: gift of the observer
+ (195046, 195047, 0, 0 , 2), -- ulduar: cache of living stone
+ (195631, 195632, 195633, 195635, 2), -- toc25: champions' cache
+ (202178, 202180, 202177, 202179, 2), -- icc: gunship armory (horde)
+ (201873, 201874, 201872, 201875, 2), -- icc: gunship armory (alliance)
+ (202239, 202240, 202238, 202241, 2), -- icc: deathbringer's cache
+ (201959, 202339, 202338, 202340, 2), -- icc: cache of the dreamwalker
+ (0, 0, 195668, 195672, 2), -- toc25: argent crusade tribute chest 1TL
+ (0, 0, 195667, 195671, 2), -- toc25: argent crusade tribute chest 25TL
+ (0, 0, 195666, 195670, 2), -- toc25: argent crusade tribute chest 45TL
+ (0, 0, 195665, 195669, 2), -- toc25: argent crusade tribute chest 50TL
+ (185168, 185169, 0, 0 , 1), -- hellfire ramparts: reinforced fel iron chest
+ (184465, 184849, 0, 0 , 1), -- mechanar: cache of the legion
+ (190586, 193996, 0, 0 , 1), -- halls of stone: tribunal chest
+ (190663, 193597, 0, 0 , 1), -- cot - cos: dark runed chest
+ (191349, 193603, 0, 0 , 1), -- oculus: cache of eregos
+ (195709, 195710, 0, 0 , 1), -- toc5: champion's cache
+ (195323, 195324, 0, 0 , 1), -- toc5: confessor's cache
+ (195374, 195375, 0, 0 , 1), -- toc5: eadric's cache
+ (201710, 202336, 0, 0 , 1); -- hor: captain's chest
diff --git a/setup/sql/updates/1763557620_02.sql b/setup/sql/updates/1763557620_02.sql
new file mode 100644
index 00000000..fb068d31
--- /dev/null
+++ b/setup/sql/updates/1763557620_02.sql
@@ -0,0 +1,18 @@
+ALTER TABLE `aowow_spelldifficulty`
+ ADD COLUMN `mapType` tinyint(3) unsigned NOT NULL AFTER `heroic25`
+;
+
+-- move linked chest for icc: gunship battle. duplicate saurfang to muradin
+DELETE FROM `aowow_loot_link` WHERE `npcId` IN (36939, 38156, 38637, 38638, 36948, 38157, 38639, 38640);
+INSERT INTO `aowow_loot_link` (`npcId`, `objectId`, `difficulty`, `priority`, `encounterId`) VALUES
+ (36939, 201873, 1, 0, 847),
+ (38156, 201874, 2, 0, 847),
+ (38637, 201872, 3, 0, 847),
+ (38638, 201875, 4, 0, 847),
+ (36948, 202178, 1, 0, 847),
+ (38157, 202180, 2, 0, 847),
+ (38639, 202177, 3, 0, 847),
+ (38640, 202179, 4, 0, 847)
+;
+
+UPDATE `aowow_dbversion` SET `sql` = CONCAT(IFNULL(`sql`, ''), ' source spelldifficulty');
diff --git a/setup/sql/updates/1763580264_01.sql b/setup/sql/updates/1763580264_01.sql
new file mode 100644
index 00000000..4b88130c
--- /dev/null
+++ b/setup/sql/updates/1763580264_01.sql
@@ -0,0 +1,2 @@
+ALTER TABLE `aowow_account_reputation`
+ MODIFY COLUMN `amount` tinyint(3) signed NOT NULL;
diff --git a/setup/sql/updates/1763677664_01.sql b/setup/sql/updates/1763677664_01.sql
new file mode 100644
index 00000000..03adf418
--- /dev/null
+++ b/setup/sql/updates/1763677664_01.sql
@@ -0,0 +1 @@
+UPDATE `aowow_dbversion` SET `build` = CONCAT(IFNULL(`build`, ''), ' globaljs');
diff --git a/setup/sql/updates/1763760598_01.sql b/setup/sql/updates/1763760598_01.sql
new file mode 100644
index 00000000..6ec5e09b
--- /dev/null
+++ b/setup/sql/updates/1763760598_01.sql
@@ -0,0 +1,2 @@
+UPDATE `aowow_dbversion` SET `build` = CONCAT(IFNULL(`build`, ''), ' globaljs');
+UPDATE `aowow_dbversion` SET `sql` = CONCAT(IFNULL(`sql`, ''), ' spell');
diff --git a/setup/sql/updates/1763850348_01.sql b/setup/sql/updates/1763850348_01.sql
new file mode 100644
index 00000000..c0c38b25
--- /dev/null
+++ b/setup/sql/updates/1763850348_01.sql
@@ -0,0 +1 @@
+UPDATE `aowow_dbversion` SET `sql` = CONCAT(IFNULL(`sql`, ''), ' spawns');
diff --git a/setup/sql/updates/1764273019_01.sql b/setup/sql/updates/1764273019_01.sql
new file mode 100644
index 00000000..9fa47c05
--- /dev/null
+++ b/setup/sql/updates/1764273019_01.sql
@@ -0,0 +1 @@
+UPDATE `aowow_dbversion` SET `sql` = CONCAT(IFNULL(`sql`, ''), ' stats');
diff --git a/setup/sql/updates/1764691622_01.sql b/setup/sql/updates/1764691622_01.sql
new file mode 100644
index 00000000..8c20c87c
--- /dev/null
+++ b/setup/sql/updates/1764691622_01.sql
@@ -0,0 +1,4 @@
+ALTER TABLE aowow_creature
+ ADD COLUMN `schoolImmuneMask` int(10) unsigned NOT NULL DEFAULT 0 AFTER `mechanicImmuneMask`;
+
+UPDATE `aowow_dbversion` SET `sql` = CONCAT(IFNULL(`sql`, ''), ' creature');
diff --git a/setup/sql/updates/1764798161_01.sql b/setup/sql/updates/1764798161_01.sql
new file mode 100644
index 00000000..0688d1f0
--- /dev/null
+++ b/setup/sql/updates/1764798161_01.sql
@@ -0,0 +1,7 @@
+ALTER TABLE aowow_icons
+ ADD COLUMN `name_source` varchar(55) NOT NULL AFTER `name`;
+
+UPDATE `aowow_dbversion` SET
+ `sql` = CONCAT(IFNULL(`sql`, ''), ' icons races classes holidays'),
+ `build` = CONCAT(IFNULL(`build`, ''), ' simpleimg')
+;
diff --git a/setup/sql/updates/1764798161_02.sql b/setup/sql/updates/1764798161_02.sql
new file mode 100644
index 00000000..139dd8be
--- /dev/null
+++ b/setup/sql/updates/1764798161_02.sql
@@ -0,0 +1,19 @@
+-- drop obsolete custom data for holiday icons
+DELETE FROM aowow_setup_custom_data WHERE `command` = 'holidays' AND `field` = 'iconString';
+UPDATE aowow_holidays SET `iconString` = '';
+
+-- support calendar_* icons
+ALTER TABLE aowow_holidays
+ CHANGE COLUMN `iconString` `iconId` smallint(5) unsigned NOT NULL DEFAULT 0
+;
+
+-- support class_* icons
+ALTER TABLE aowow_classes
+ ADD COLUMN `iconId` smallint(5) unsigned NOT NULL DEFAULT 0 AFTER `fileString`
+;
+
+-- support race_* icons
+ALTER TABLE aowow_races
+ ADD COLUMN `iconId0` smallint(5) unsigned NOT NULL DEFAULT 0 COMMENT "male icon" AFTER `fileString`,
+ ADD COLUMN `iconId1` smallint(5) unsigned NOT NULL DEFAULT 0 COMMENT "female icon" AFTER `iconId0`
+;
diff --git a/setup/sql/updates/1764966675_01.sql b/setup/sql/updates/1764966675_01.sql
new file mode 100644
index 00000000..bef0e1a9
--- /dev/null
+++ b/setup/sql/updates/1764966675_01.sql
@@ -0,0 +1 @@
+UPDATE `aowow_dbversion` SET `sql` = CONCAT(IFNULL(`sql`, ''), ' source');
diff --git a/setup/sql/updates/1765116606_01.sql b/setup/sql/updates/1765116606_01.sql
new file mode 100644
index 00000000..03adf418
--- /dev/null
+++ b/setup/sql/updates/1765116606_01.sql
@@ -0,0 +1 @@
+UPDATE `aowow_dbversion` SET `build` = CONCAT(IFNULL(`build`, ''), ' globaljs');
diff --git a/setup/sql/updates/1765569409_01.sql b/setup/sql/updates/1765569409_01.sql
new file mode 100644
index 00000000..03adf418
--- /dev/null
+++ b/setup/sql/updates/1765569409_01.sql
@@ -0,0 +1 @@
+UPDATE `aowow_dbversion` SET `build` = CONCAT(IFNULL(`build`, ''), ' globaljs');
diff --git a/setup/sql/updates/1767026729_01.sql b/setup/sql/updates/1767026729_01.sql
new file mode 100644
index 00000000..276abad4
--- /dev/null
+++ b/setup/sql/updates/1767026729_01.sql
@@ -0,0 +1,18 @@
+ALTER TABLE `aowow_spawns`
+ ADD COLUMN `ScriptName` varchar(64) DEFAULT NULL AFTER `pathId`,
+ ADD COLUMN `StringId` varchar(64) DEFAULT NULL AFTER `ScriptName`
+;
+
+ALTER TABLE `aowow_objects`
+ MODIFY COLUMN `ScriptOrAI` varchar(64) DEFAULT NULL,
+ ADD COLUMN `StringId` varchar(64) DEFAULT NULL AFTER `ScriptOrAI`
+;
+
+ALTER TABLE `aowow_creature`
+ DROP COLUMN `aiName`,
+ DROP COLUMN `scriptName`,
+ ADD COLUMN `ScriptOrAI` varchar(64) DEFAULT NULL AFTER `flagsExtra`,
+ ADD COLUMN `StringId` varchar(64) DEFAULT NULL AFTER `ScriptOrAI`
+;
+
+UPDATE `aowow_dbversion` SET `sql` = CONCAT(IFNULL(`sql`, ''), ' creature objects spawns');
diff --git a/setup/sql/updates/1767034443_01.sql b/setup/sql/updates/1767034443_01.sql
new file mode 100644
index 00000000..03adf418
--- /dev/null
+++ b/setup/sql/updates/1767034443_01.sql
@@ -0,0 +1 @@
+UPDATE `aowow_dbversion` SET `build` = CONCAT(IFNULL(`build`, ''), ' globaljs');
diff --git a/setup/sql/updates/1767051301_01.sql b/setup/sql/updates/1767051301_01.sql
new file mode 100644
index 00000000..1e13478c
--- /dev/null
+++ b/setup/sql/updates/1767051301_01.sql
@@ -0,0 +1,4 @@
+DELETE FROM aowow_config WHERE `key` = 'sql_limit_default';
+DELETE FROM aowow_config WHERE `key` = 'sql_limit_none';
+DELETE FROM aowow_config WHERE `key` = 'sql_limit_quicksearch';
+DELETE FROM aowow_config WHERE `key` = 'sql_limit_search';
diff --git a/setup/sql/updates/1767117346_01.sql b/setup/sql/updates/1767117346_01.sql
new file mode 100644
index 00000000..5a565565
--- /dev/null
+++ b/setup/sql/updates/1767117346_01.sql
@@ -0,0 +1,6 @@
+ALTER TABLE aowow_quests
+ CHANGE COLUMN `method` `questType` tinyint(3) unsigned NOT NULL DEFAULT 2,
+ CHANGE COLUMN `zoneOrSort` `questSortId` smallint(6) NOT NULL DEFAULT 0,
+ CHANGE COLUMN `zoneOrSortBak` `questSortIdBak` smallint(6) NOT NULL DEFAULT 0,
+ CHANGE COLUMN `type` `questInfoId` smallint(5) unsigned NOT NULL DEFAULT 0
+;
diff --git a/setup/sql/updates/1767117346_02.sql b/setup/sql/updates/1767117346_02.sql
new file mode 100644
index 00000000..ac8b6c36
--- /dev/null
+++ b/setup/sql/updates/1767117346_02.sql
@@ -0,0 +1 @@
+UPDATE aowow_setup_custom_data SET `field` = 'questSortId' WHERE `field` = 'zoneOrSort';
diff --git a/setup/sql/updates/1768155583_01.sql b/setup/sql/updates/1768155583_01.sql
new file mode 100644
index 00000000..03adf418
--- /dev/null
+++ b/setup/sql/updates/1768155583_01.sql
@@ -0,0 +1 @@
+UPDATE `aowow_dbversion` SET `build` = CONCAT(IFNULL(`build`, ''), ' globaljs');
diff --git a/setup/sql/updates/1768324765_01.sql b/setup/sql/updates/1768324765_01.sql
new file mode 100644
index 00000000..3b04a1df
--- /dev/null
+++ b/setup/sql/updates/1768324765_01.sql
@@ -0,0 +1,9 @@
+ALTER TABLE `aowow_taxinodes`
+ CHANGE COLUMN `posX` `mapX` float unsigned NOT NULL,
+ CHANGE COLUMN `posY` `mapY` float unsigned NOT NULL,
+ ADD COLUMN `areaId` smallint(5) unsigned NOT NULL AFTER `mapY`,
+ ADD COLUMN `areaX` float unsigned NOT NULL AFTER `areaId`,
+ ADD COLUMN `areaY` float unsigned NOT NULL AFTER `areaX`
+;
+
+UPDATE `aowow_dbversion` SET `sql` = CONCAT(IFNULL(`sql`, ''), ' taxi');
diff --git a/setup/sql/updates/1768385060_01.sql b/setup/sql/updates/1768385060_01.sql
new file mode 100644
index 00000000..06dea004
--- /dev/null
+++ b/setup/sql/updates/1768385060_01.sql
@@ -0,0 +1,20 @@
+ALTER TABLE `aowow_profiler_profiles`
+ ADD INDEX idx_race (`race`),
+ ADD INDEX idx_class (`class`),
+ ADD INDEX idx_level (`level`),
+ ADD INDEX idx_guildrank (`guildrank`),
+ ADD INDEX idx_gearscore (`gearscore`),
+ ADD INDEX idx_achievementpoints (`achievementpoints`),
+ ADD INDEX idx_talenttree1 (`talenttree1`),
+ ADD INDEX idx_talenttree2 (`talenttree2`),
+ ADD INDEX idx_talenttree3 (`talenttree3`)
+;
+
+ALTER TABLE aowow_profiler_completion_skills
+ ADD INDEX idx_value (`value`)
+;
+
+ALTER TABLE aowow_profiler_arena_team
+ ADD INDEX idx_type (`type`),
+ ADD INDEX idx_rating (`rating`)
+;
diff --git a/setup/sql/updates/1768517243_01.sql b/setup/sql/updates/1768517243_01.sql
new file mode 100644
index 00000000..e2fa5d5c
--- /dev/null
+++ b/setup/sql/updates/1768517243_01.sql
@@ -0,0 +1,9 @@
+UPDATE `aowow_items` SET
+ `requiredClass` = IF((`requiredClass` & 1535) = 1535, 0, `requiredClass` & 1535),
+ `requiredRace` = IF((`requiredRace` & 1791) = 1791, 0, `requiredRace` & 1791)
+;
+
+ALTER TABLE `aowow_items`
+ MODIFY COLUMN `requiredClass` smallint(5) unsigned NOT NULL DEFAULT 0,
+ MODIFY COLUMN `requiredRace` smallint(5) unsigned NOT NULL DEFAULT 0
+;
diff --git a/setup/sql/updates/1768556688_01.sql b/setup/sql/updates/1768556688_01.sql
new file mode 100644
index 00000000..b1e65dfb
--- /dev/null
+++ b/setup/sql/updates/1768556688_01.sql
@@ -0,0 +1,120 @@
+ALTER TABLE `aowow_spell`
+ DROP INDEX `items`,
+ DROP INDEX `effects`,
+ ADD INDEX `idx_skill1` (`skillLine1`),
+ ADD INDEX `idx_skill2` (`skillLine2OrMask`),
+ ADD FULLTEXT `idx_name0` (`name_loc0`),
+ ADD FULLTEXT `idx_name2` (`name_loc2`),
+ ADD FULLTEXT `idx_name3` (`name_loc3`),
+ ADD FULLTEXT `idx_name4` (`name_loc4`),
+ ADD FULLTEXT `idx_name6` (`name_loc6`),
+ ADD FULLTEXT `idx_name8` (`name_loc8`),
+ ADD INDEX `idx_spellfamily` (`spellFamilyId`),
+ ADD INDEX `idx_miscvalue1` (`effect1MiscValue`),
+ ADD INDEX `idx_miscvalue2` (`effect2MiscValue`),
+ ADD INDEX `idx_miscvalue3` (`effect3MiscValue`),
+ ADD INDEX `idx_triggerspell1` (`effect1TriggerSpell`),
+ ADD INDEX `idx_triggerspell2` (`effect2TriggerSpell`),
+ ADD INDEX `idx_triggerspell3` (`effect3TriggerSpell`)
+;
+
+ALTER TABLE `aowow_quests`
+ MODIFY COLUMN `name_loc0` varchar(100) DEFAULT NULL,
+ MODIFY COLUMN `name_loc2` varchar(100) DEFAULT NULL,
+ MODIFY COLUMN `name_loc3` varchar(100) DEFAULT NULL,
+ MODIFY COLUMN `name_loc4` varchar(100) DEFAULT NULL,
+ MODIFY COLUMN `name_loc6` varchar(100) DEFAULT NULL,
+ MODIFY COLUMN `name_loc8` varchar(100) DEFAULT NULL,
+ ADD FULLTEXT `idx_name0` (`name_loc0`),
+ ADD FULLTEXT `idx_name2` (`name_loc2`),
+ ADD FULLTEXT `idx_name3` (`name_loc3`),
+ ADD FULLTEXT `idx_name4` (`name_loc4`),
+ ADD FULLTEXT `idx_name6` (`name_loc6`),
+ ADD FULLTEXT `idx_name8` (`name_loc8`),
+ ADD INDEX `idx_sourcespell` (`sourceSpellId`),
+ ADD INDEX `idx_rewardspell` (`rewardSpell`),
+ ADD INDEX `idx_rewardcastspell` (`rewardSpellCast`),
+ ADD INDEX `idx_classmask` (`reqRaceMask`),
+ ADD INDEX `idx_racemask` (`reqClassMask`),
+ ADD INDEX `idx_questsort` (`questSortId`),
+ ADD INDEX `idx_rewarditem1` (`rewardChoiceItemId1`),
+ ADD INDEX `idx_rewarditem2` (`rewardChoiceItemId2`),
+ ADD INDEX `idx_rewarditem3` (`rewardChoiceItemId3`),
+ ADD INDEX `idx_rewarditem4` (`rewardChoiceItemId4`),
+ ADD INDEX `idx_rewarditem5` (`rewardChoiceItemId5`),
+ ADD INDEX `idx_rewarditem6` (`rewardChoiceItemId6`),
+ ADD INDEX `idx_rewardfaction1` (`rewardFactionId1`),
+ ADD INDEX `idx_rewardfaction2` (`rewardFactionId2`),
+ ADD INDEX `idx_rewardfaction3` (`rewardFactionId3`),
+ ADD INDEX `idx_rewardfaction4` (`rewardFactionId4`),
+ ADD INDEX `idx_rewardfaction5` (`rewardFactionId5`),
+ ADD INDEX `idx_choiceitem1` (`rewardItemId1`),
+ ADD INDEX `idx_choiceitem2` (`rewardItemId2`),
+ ADD INDEX `idx_choiceitem3` (`rewardItemId3`),
+ ADD INDEX `idx_choiceitem4` (`rewardItemId4`),
+ ADD INDEX `idx_requirement1` (`reqNpcOrGo1`),
+ ADD INDEX `idx_requirement2` (`reqNpcOrGo2`),
+ ADD INDEX `idx_requirement3` (`reqNpcOrGo3`),
+ ADD INDEX `idx_requirement4` (`reqNpcOrGo4`),
+ ADD INDEX `idx_event` (`eventId`)
+;
+
+ALTER TABLE `aowow_creature`
+ DROP INDEX `idx_name`,
+ ADD INDEX `idx_trainer` (`trainerType`),
+ ADD INDEX `idx_trainerrequirement` (`trainerRequirement`),
+ ADD FULLTEXT `idx_name0` (`name_loc0`),
+ ADD FULLTEXT `idx_name2` (`name_loc2`),
+ ADD FULLTEXT `idx_name3` (`name_loc3`),
+ ADD FULLTEXT `idx_name4` (`name_loc4`),
+ ADD FULLTEXT `idx_name6` (`name_loc6`),
+ ADD FULLTEXT `idx_name8` (`name_loc8`),
+ ADD INDEX `idx_spell1` (`spell1`),
+ ADD INDEX `idx_spell2` (`spell2`),
+ ADD INDEX `idx_spell3` (`spell3`),
+ ADD INDEX `idx_spell4` (`spell4`),
+ ADD INDEX `idx_spell5` (`spell5`),
+ ADD INDEX `idx_spell6` (`spell6`),
+ ADD INDEX `idx_spell7` (`spell7`),
+ ADD INDEX `idx_spell8` (`spell8`)
+;
+
+ALTER TABLE `aowow_items`
+ DROP INDEX `spellId1`,
+ DROP INDEX `spellId2`,
+ DROP INDEX `idx_name`,
+ ADD INDEX `idx_spell1` (`spellId1`),
+ ADD INDEX `idx_spell2` (`spellId2`),
+ ADD INDEX `idx_spell3` (`spellId3`),
+ ADD INDEX `idx_spell4` (`spellId4`),
+ ADD INDEX `idx_spell5` (`spellId5`),
+ ADD INDEX `idx_trigger1` (`spellTrigger1`),
+ ADD INDEX `idx_trigger2` (`spellTrigger2`),
+ ADD INDEX `idx_trigger3` (`spellTrigger3`),
+ ADD INDEX `idx_trigger4` (`spellTrigger4`),
+ ADD INDEX `idx_trigger5` (`spellTrigger5`),
+ ADD INDEX `idx_reqskill` (`requiredSkill`),
+ ADD FULLTEXT `idx_name0` (`name_loc0`),
+ ADD FULLTEXT `idx_name2` (`name_loc2`),
+ ADD FULLTEXT `idx_name3` (`name_loc3`),
+ ADD FULLTEXT `idx_name4` (`name_loc4`),
+ ADD FULLTEXT `idx_name6` (`name_loc6`),
+ ADD FULLTEXT `idx_name8` (`name_loc8`),
+ ADD INDEX `idx_itemset` (`itemset`)
+;
+
+ALTER TABLE `aowow_objects`
+ DROP INDEX `idx_name`,
+ ADD INDEX `idx_onusespell` (`onUseSpell`),
+ ADD INDEX `idx_onsuccessspell` (`onSuccessSpell`),
+ ADD INDEX `idx_auraspell` (`auraSpell`),
+ ADD INDEX `idx_triggeredspell` (`triggeredSpell`),
+ ADD FULLTEXT `idx_name0` (`name_loc0`),
+ ADD FULLTEXT `idx_name2` (`name_loc2`),
+ ADD FULLTEXT `idx_name3` (`name_loc3`),
+ ADD FULLTEXT `idx_name4` (`name_loc4`),
+ ADD FULLTEXT `idx_name6` (`name_loc6`),
+ ADD FULLTEXT `idx_name8` (`name_loc8`)
+;
+
+UPDATE `aowow_dbversion` SET `sql` = CONCAT(IFNULL(`sql`, ''), ' achievementcriteria');
diff --git a/setup/sql/updates/1768672799_01.sql b/setup/sql/updates/1768672799_01.sql
new file mode 100644
index 00000000..140bf2d6
--- /dev/null
+++ b/setup/sql/updates/1768672799_01.sql
@@ -0,0 +1,16 @@
+ALTER TABLE `aowow_creature` DROP INDEX `idx_name4`;
+ALTER TABLE `aowow_items` DROP INDEX `idx_name4`;
+ALTER TABLE `aowow_objects` DROP INDEX `idx_name4`;
+ALTER TABLE `aowow_quests` DROP INDEX `idx_name4`;
+ALTER TABLE `aowow_spell` DROP INDEX `idx_name4`;
+
+SET SESSION innodb_ft_enable_stopword = OFF;
+
+OPTIMIZE TABLE `aowow_spell`;
+OPTIMIZE TABLE `aowow_quests`;
+OPTIMIZE TABLE `aowow_creature`;
+OPTIMIZE TABLE `aowow_items`;
+OPTIMIZE TABLE `aowow_objects`;
+
+REPLACE INTO `aowow_config` VALUES
+ ('logographic_ft_search', '0', '0', 1, 0x484, 'enables fulltext search for logographic languages (CN, KR, TW). The database MUST support this (i.e. MySQL implements ngram)');
diff --git a/setup/sql/updates/1769190110_01.sql b/setup/sql/updates/1769190110_01.sql
new file mode 100644
index 00000000..4aced6c3
--- /dev/null
+++ b/setup/sql/updates/1769190110_01.sql
@@ -0,0 +1 @@
+UPDATE `aowow_dbversion` SET `sql` = CONCAT(IFNULL(`sql`, ''), ' quests');
diff --git a/setup/sql/updates/1769622382_01.sql b/setup/sql/updates/1769622382_01.sql
new file mode 100644
index 00000000..fecbced8
--- /dev/null
+++ b/setup/sql/updates/1769622382_01.sql
@@ -0,0 +1,6 @@
+ALTER TABLE `aowow_icons` ADD KEY idx_sourcename (`name_source`);
+
+UPDATE `aowow_dbversion` SET
+ `sql` = CONCAT(IFNULL(`sql`, ''), ' achievement currencies glyphproperties holidays icons items pet skillline spell'),
+ `build` = CONCAT(IFNULL(`build`, ''), ' enchants gems glyphs talenticons')
+;
diff --git a/setup/sql/updates/1770309983_01.sql b/setup/sql/updates/1770309983_01.sql
new file mode 100644
index 00000000..27c9e220
--- /dev/null
+++ b/setup/sql/updates/1770309983_01.sql
@@ -0,0 +1 @@
+UPDATE `aowow_dbversion` SET `build` = CONCAT(IFNULL(`build`, ''), ' spellscaling itemscaling');
diff --git a/setup/sql/updates/1770626911_01.sql b/setup/sql/updates/1770626911_01.sql
new file mode 100644
index 00000000..a71c1fa1
--- /dev/null
+++ b/setup/sql/updates/1770626911_01.sql
@@ -0,0 +1,91 @@
+SET SESSION innodb_ft_enable_stopword = OFF;
+
+ALTER TABLE aowow_creature
+ DROP INDEX idx_name0,
+ DROP INDEX idx_name2,
+ DROP INDEX idx_name3,
+ DROP INDEX idx_name6,
+ DROP INDEX idx_name8,
+ ADD INDEX idx_name0 (`name_loc0`),
+ ADD INDEX idx_name2 (`name_loc2`),
+ ADD INDEX idx_name3 (`name_loc3`),
+ ADD INDEX idx_name4 (`name_loc4`),
+ ADD INDEX idx_name6 (`name_loc6`),
+ ADD INDEX idx_name8 (`name_loc8`),
+ ADD FULLTEXT idx_ft_name0 (`name_loc0`),
+ ADD FULLTEXT idx_ft_name2 (`name_loc2`),
+ ADD FULLTEXT idx_ft_name3 (`name_loc3`),
+ ADD FULLTEXT idx_ft_name6 (`name_loc6`),
+ ADD FULLTEXT idx_ft_name8 (`name_loc8`);
+
+ALTER TABLE aowow_items
+ DROP INDEX idx_name0,
+ DROP INDEX idx_name2,
+ DROP INDEX idx_name3,
+ DROP INDEX idx_name6,
+ DROP INDEX idx_name8,
+ ADD INDEX idx_name0 (`name_loc0`),
+ ADD INDEX idx_name2 (`name_loc2`),
+ ADD INDEX idx_name3 (`name_loc3`),
+ ADD INDEX idx_name4 (`name_loc4`),
+ ADD INDEX idx_name6 (`name_loc6`),
+ ADD INDEX idx_name8 (`name_loc8`),
+ ADD FULLTEXT idx_ft_name0 (`name_loc0`),
+ ADD FULLTEXT idx_ft_name2 (`name_loc2`),
+ ADD FULLTEXT idx_ft_name3 (`name_loc3`),
+ ADD FULLTEXT idx_ft_name6 (`name_loc6`),
+ ADD FULLTEXT idx_ft_name8 (`name_loc8`);
+
+ALTER TABLE aowow_objects
+ DROP INDEX idx_name0,
+ DROP INDEX idx_name2,
+ DROP INDEX idx_name3,
+ DROP INDEX idx_name6,
+ DROP INDEX idx_name8,
+ ADD INDEX idx_name0 (`name_loc0`),
+ ADD INDEX idx_name2 (`name_loc2`),
+ ADD INDEX idx_name3 (`name_loc3`),
+ ADD INDEX idx_name4 (`name_loc4`),
+ ADD INDEX idx_name6 (`name_loc6`),
+ ADD INDEX idx_name8 (`name_loc8`),
+ ADD FULLTEXT idx_ft_name0 (`name_loc0`),
+ ADD FULLTEXT idx_ft_name2 (`name_loc2`),
+ ADD FULLTEXT idx_ft_name3 (`name_loc3`),
+ ADD FULLTEXT idx_ft_name6 (`name_loc6`),
+ ADD FULLTEXT idx_ft_name8 (`name_loc8`);
+
+ALTER TABLE aowow_quests
+ DROP INDEX idx_name0,
+ DROP INDEX idx_name2,
+ DROP INDEX idx_name3,
+ DROP INDEX idx_name6,
+ DROP INDEX idx_name8,
+ ADD INDEX idx_name0 (`name_loc0`),
+ ADD INDEX idx_name2 (`name_loc2`),
+ ADD INDEX idx_name3 (`name_loc3`),
+ ADD INDEX idx_name4 (`name_loc4`),
+ ADD INDEX idx_name6 (`name_loc6`),
+ ADD INDEX idx_name8 (`name_loc8`),
+ ADD FULLTEXT idx_ft_name0 (`name_loc0`),
+ ADD FULLTEXT idx_ft_name2 (`name_loc2`),
+ ADD FULLTEXT idx_ft_name3 (`name_loc3`),
+ ADD FULLTEXT idx_ft_name6 (`name_loc6`),
+ ADD FULLTEXT idx_ft_name8 (`name_loc8`);
+
+ALTER TABLE aowow_spell
+ DROP INDEX idx_name0,
+ DROP INDEX idx_name2,
+ DROP INDEX idx_name3,
+ DROP INDEX idx_name6,
+ DROP INDEX idx_name8,
+ ADD INDEX idx_name0 (`name_loc0`),
+ ADD INDEX idx_name2 (`name_loc2`),
+ ADD INDEX idx_name3 (`name_loc3`),
+ ADD INDEX idx_name4 (`name_loc4`),
+ ADD INDEX idx_name6 (`name_loc6`),
+ ADD INDEX idx_name8 (`name_loc8`),
+ ADD FULLTEXT idx_ft_name0 (`name_loc0`),
+ ADD FULLTEXT idx_ft_name2 (`name_loc2`),
+ ADD FULLTEXT idx_ft_name3 (`name_loc3`),
+ ADD FULLTEXT idx_ft_name6 (`name_loc6`),
+ ADD FULLTEXT idx_ft_name8 (`name_loc8`);
diff --git a/setup/sql/updates/1770889048_01.sql b/setup/sql/updates/1770889048_01.sql
new file mode 100644
index 00000000..54df351e
--- /dev/null
+++ b/setup/sql/updates/1770889048_01.sql
@@ -0,0 +1,9 @@
+ALTER TABLE aowow_items
+ ADD COLUMN `effects_loc0` text DEFAULT NULL AFTER `flagsCustom`,
+ ADD COLUMN `effects_loc2` text DEFAULT NULL AFTER `effects_loc0`,
+ ADD COLUMN `effects_loc3` text DEFAULT NULL AFTER `effects_loc2`,
+ ADD COLUMN `effects_loc4` text DEFAULT NULL AFTER `effects_loc3`,
+ ADD COLUMN `effects_loc6` text DEFAULT NULL AFTER `effects_loc4`,
+ ADD COLUMN `effects_loc8` text DEFAULT NULL AFTER `effects_loc6`;
+
+UPDATE `aowow_dbversion` SET `sql` = CONCAT(IFNULL(`sql`, ''), ' items');
diff --git a/setup/sql/updates/1771934998_01.sql b/setup/sql/updates/1771934998_01.sql
new file mode 100644
index 00000000..9fa47c05
--- /dev/null
+++ b/setup/sql/updates/1771934998_01.sql
@@ -0,0 +1 @@
+UPDATE `aowow_dbversion` SET `sql` = CONCAT(IFNULL(`sql`, ''), ' stats');
diff --git a/setup/sql/updates/1772564118_01.sql b/setup/sql/updates/1772564118_01.sql
new file mode 100644
index 00000000..9c80c4e1
--- /dev/null
+++ b/setup/sql/updates/1772564118_01.sql
@@ -0,0 +1,56 @@
+DROP TABLE IF EXISTS `aowow_quests_search`;
+CREATE TABLE `aowow_quests_search` (
+ `id` mediumint(8) unsigned NOT NULL,
+ `locale` tinyint(3) unsigned NOT NULL,
+ `nName` varchar(100) DEFAULT NULL,
+ `nObjectives` text DEFAULT NULL,
+ `nDetails` text DEFAULT NULL,
+ PRIMARY KEY (`id`, `locale`),
+ FULLTEXT `idx_ft_na` (`nName`),
+ FULLTEXT `idx_ft_na_ex` (`nName`, `nObjectives`, `nDetails`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+
+DROP TABLE IF EXISTS `aowow_objects_search`;
+CREATE TABLE `aowow_objects_search` (
+ `id` mediumint(8) unsigned NOT NULL,
+ `locale` tinyint(3) unsigned NOT NULL,
+ `nName` varchar(127) DEFAULT NULL,
+ PRIMARY KEY (`id`, `locale`),
+ FULLTEXT `idx_ft_na` (`nName`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+
+DROP TABLE IF EXISTS `aowow_items_search`;
+CREATE TABLE `aowow_items_search` (
+ `id` mediumint(8) unsigned NOT NULL,
+ `locale` tinyint(3) unsigned NOT NULL,
+ `nName` varchar(127) DEFAULT NULL,
+ `nDescription` varchar(255) DEFAULT NULL,
+ `nEffects` text DEFAULT NULL,
+ PRIMARY KEY (`id`, `locale`),
+ FULLTEXT `idx_ft_na` (`nName`),
+ FULLTEXT `idx_ft_description` (`nDescription`),
+ FULLTEXT `idx_ft_effects` (`nEffects`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+
+DROP TABLE IF EXISTS `aowow_creature_search`;
+CREATE TABLE `aowow_creature_search` (
+ `id` mediumint(8) unsigned NOT NULL,
+ `locale` tinyint(3) unsigned NOT NULL,
+ `nName` varchar(100) DEFAULT NULL,
+ `nSubname` varchar(100) DEFAULT NULL,
+ PRIMARY KEY (`id`, `locale`),
+ FULLTEXT `idx_ft_na` (`nName`),
+ FULLTEXT `idx_ft_na_ex` (`nName`, `nSubname`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+
+DROP TABLE IF EXISTS `aowow_spell_search`;
+CREATE TABLE `aowow_spell_search` (
+ `id` mediumint(8) unsigned NOT NULL,
+ `locale` tinyint(3) unsigned NOT NULL,
+ `nName` varchar(185) DEFAULT NULL,
+ `nDescription` text DEFAULT NULL,
+ `nBuff` text DEFAULT NULL,
+ PRIMARY KEY (`id`, `locale`),
+ FULLTEXT `idx_ft_na` (`nName`),
+ FULLTEXT `idx_ft_na_ex` (`nName`, `nDescription`, `nBuff`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
diff --git a/setup/sql/updates/1772564118_02.sql b/setup/sql/updates/1772564118_02.sql
new file mode 100644
index 00000000..55f353a7
--- /dev/null
+++ b/setup/sql/updates/1772564118_02.sql
@@ -0,0 +1,40 @@
+ALTER TABLE `aowow_creature`
+ DROP INDEX `idx_ft_name0`,
+ DROP INDEX `idx_ft_name2`,
+ DROP INDEX `idx_ft_name3`,
+ DROP INDEX `idx_ft_name6`,
+ DROP INDEX `idx_ft_name8`;
+
+ALTER TABLE `aowow_objects`
+ DROP INDEX `idx_ft_name0`,
+ DROP INDEX `idx_ft_name2`,
+ DROP INDEX `idx_ft_name3`,
+ DROP INDEX `idx_ft_name6`,
+ DROP INDEX `idx_ft_name8`;
+
+ALTER TABLE `aowow_quests`
+ DROP INDEX `idx_ft_name0`,
+ DROP INDEX `idx_ft_name2`,
+ DROP INDEX `idx_ft_name3`,
+ DROP INDEX `idx_ft_name6`,
+ DROP INDEX `idx_ft_name8`;
+
+ALTER TABLE `aowow_spell`
+ DROP INDEX `idx_ft_name0`,
+ DROP INDEX `idx_ft_name2`,
+ DROP INDEX `idx_ft_name3`,
+ DROP INDEX `idx_ft_name6`,
+ DROP INDEX `idx_ft_name8`;
+
+ALTER TABLE `aowow_items`
+ DROP COLUMN `effects_loc0`,
+ DROP COLUMN `effects_loc2`,
+ DROP COLUMN `effects_loc3`,
+ DROP COLUMN `effects_loc4`,
+ DROP COLUMN `effects_loc6`,
+ DROP COLUMN `effects_loc8`,
+ DROP INDEX `idx_ft_name0`,
+ DROP INDEX `idx_ft_name2`,
+ DROP INDEX `idx_ft_name3`,
+ DROP INDEX `idx_ft_name6`,
+ DROP INDEX `idx_ft_name8`;
diff --git a/setup/sql/updates/1774468408_01.sql b/setup/sql/updates/1774468408_01.sql
new file mode 100644
index 00000000..f30e8121
--- /dev/null
+++ b/setup/sql/updates/1774468408_01.sql
@@ -0,0 +1 @@
+UPDATE `aowow_dbversion` SET `sql` = CONCAT(IFNULL(`sql`, ''), ' search');
diff --git a/setup/sql/updates/1774551739_01.sql b/setup/sql/updates/1774551739_01.sql
new file mode 100644
index 00000000..debc3e95
--- /dev/null
+++ b/setup/sql/updates/1774551739_01.sql
@@ -0,0 +1,135 @@
+INSERT INTO `aowow_setup_custom_data` (`command`, `entry`, `field`, `value`, `comment`) VALUES
+ ('spell', 17579, 'cuFlags', 1610612736, 'Alchemy: Greater Holy Protection Potion - set: CUSTOM_UNAVAILABLE | CUSTOM_EXCLUDE_FOR_LISTVIEW'),
+ ('spell', 54020, 'cuFlags', 1610612736, 'Alchemy: Transmute: Eternal Might - set: CUSTOM_UNAVAILABLE | CUSTOM_EXCLUDE_FOR_LISTVIEW'),
+ ('spell', 2336, 'cuFlags', 1610612736, 'Alchemy: Elixir of Tongues - set: CUSTOM_UNAVAILABLE | CUSTOM_EXCLUDE_FOR_LISTVIEW'),
+ ('items', 13460, 'cuFlags', 1610612736, 'Greater Holy Protection Potion - set: CUSTOM_UNAVAILABLE | CUSTOM_EXCLUDE_FOR_LISTVIEW'),
+ ('items', 40248, 'cuFlags', 1610612736, 'Eternal Might - set: CUSTOM_UNAVAILABLE | CUSTOM_EXCLUDE_FOR_LISTVIEW'),
+ ('items', 2460, 'cuFlags', 1610612736, 'Elixir of Tongues - set: CUSTOM_UNAVAILABLE | CUSTOM_EXCLUDE_FOR_LISTVIEW'),
+ ('spell', 8366, 'cuFlags', 1610612736, 'Blacksmithing: Ironforge Chain - set: CUSTOM_UNAVAILABLE | CUSTOM_EXCLUDE_FOR_LISTVIEW'),
+ ('spell', 2671, 'cuFlags', 1610612736, 'Blacksmithing: Rough Bronze Bracers - set: CUSTOM_UNAVAILABLE | CUSTOM_EXCLUDE_FOR_LISTVIEW'),
+ ('spell', 8368, 'cuFlags', 1610612736, 'Blacksmithing: Ironforge Gauntlets - set: CUSTOM_UNAVAILABLE | CUSTOM_EXCLUDE_FOR_LISTVIEW'),
+ ('spell', 9942, 'cuFlags', 1610612736, 'Blacksmithing: Mithril Scale Gloves - set: CUSTOM_UNAVAILABLE | CUSTOM_EXCLUDE_FOR_LISTVIEW'),
+ ('spell', 16960, 'cuFlags', 1610612736, 'Blacksmithing: Thorium Greatsword - set: CUSTOM_UNAVAILABLE | CUSTOM_EXCLUDE_FOR_LISTVIEW'),
+ ('spell', 16965, 'cuFlags', 1610612736, 'Blacksmithing: Bleakwood Hew - set: CUSTOM_UNAVAILABLE | CUSTOM_EXCLUDE_FOR_LISTVIEW'),
+ ('spell', 16967, 'cuFlags', 1610612736, 'Blacksmithing: Inlaid Thorium Hammer - set: CUSTOM_UNAVAILABLE | CUSTOM_EXCLUDE_FOR_LISTVIEW'),
+ ('spell', 16980, 'cuFlags', 1610612736, 'Blacksmithing: Rune Edge - set: CUSTOM_UNAVAILABLE | CUSTOM_EXCLUDE_FOR_LISTVIEW'),
+ ('spell', 28244, 'cuFlags', 536870912, 'Blacksmithing: Icebane Bracers - set: CUSTOM_UNAVAILABLE'),
+ ('spell', 28242, 'cuFlags', 536870912, 'Blacksmithing: Icebane Breastplate - set: CUSTOM_UNAVAILABLE'),
+ ('spell', 28243, 'cuFlags', 536870912, 'Blacksmithing: Icebane Gauntlets - set: CUSTOM_UNAVAILABLE'),
+ ('spell', 16986, 'cuFlags', 1610612736, 'Blacksmithing: Blood Talon - set: CUSTOM_UNAVAILABLE | CUSTOM_EXCLUDE_FOR_LISTVIEW'),
+ ('spell', 16987, 'cuFlags', 1610612736, 'Blacksmithing: Darkspear - set: CUSTOM_UNAVAILABLE | CUSTOM_EXCLUDE_FOR_LISTVIEW'),
+ ('items', 2867, 'cuFlags', 1610612736, 'Rough Bronze Bracers - set: CUSTOM_UNAVAILABLE | CUSTOM_EXCLUDE_FOR_LISTVIEW'),
+ ('items', 6730, 'cuFlags', 1610612736, 'Ironforge Chain - set: CUSTOM_UNAVAILABLE | CUSTOM_EXCLUDE_FOR_LISTVIEW'),
+ ('items', 6733, 'cuFlags', 1610612736, 'Ironforge Gauntlets - set: CUSTOM_UNAVAILABLE | CUSTOM_EXCLUDE_FOR_LISTVIEW'),
+ ('items', 7925, 'cuFlags', 1610612736, 'Mithril Scale Gloves - set: CUSTOM_UNAVAILABLE | CUSTOM_EXCLUDE_FOR_LISTVIEW'),
+ ('items', 12764, 'cuFlags', 1610612736, 'Thorium Greatsword - set: CUSTOM_UNAVAILABLE | CUSTOM_EXCLUDE_FOR_LISTVIEW'),
+ ('items', 12769, 'cuFlags', 1610612736, 'Bleakwood Hew - set: CUSTOM_UNAVAILABLE | CUSTOM_EXCLUDE_FOR_LISTVIEW'),
+ ('items', 12772, 'cuFlags', 1610612736, 'Inlaid Thorium Hammer - set: CUSTOM_UNAVAILABLE | CUSTOM_EXCLUDE_FOR_LISTVIEW'),
+ ('items', 12779, 'cuFlags', 1610612736, 'Rune Edge - set: CUSTOM_UNAVAILABLE | CUSTOM_EXCLUDE_FOR_LISTVIEW'),
+ ('items', 12795, 'cuFlags', 1610612736, 'Blood Talon - set: CUSTOM_UNAVAILABLE | CUSTOM_EXCLUDE_FOR_LISTVIEW'),
+ ('items', 12802, 'cuFlags', 1610612736, 'Darkspear - set: CUSTOM_UNAVAILABLE | CUSTOM_EXCLUDE_FOR_LISTVIEW'),
+ ('items', 22671, 'cuFlags', 536870912, 'Icebane Bracers - set: CUSTOM_UNAVAILABLE'),
+ ('items', 22669, 'cuFlags', 536870912, 'Icebane Breastplate - set: CUSTOM_UNAVAILABLE'),
+ ('items', 22670, 'cuFlags', 536870912, 'Icebane Gauntlets - set: CUSTOM_UNAVAILABLE'),
+ ('spell', 28021, 'cuFlags', 1610612736, 'Enchanting: Arcane Dust - set: CUSTOM_UNAVAILABLE | CUSTOM_EXCLUDE_FOR_LISTVIEW'),
+ ('spell', 44612, 'cuFlags', 1610612736, 'Enchanting: Enchant Gloves - Greater Blasting - set: CUSTOM_UNAVAILABLE | CUSTOM_EXCLUDE_FOR_LISTVIEW'),
+ ('spell', 62257, 'cuFlags', 1610612736, 'Enchanting: Enchant Weapon - Titanguard - set: CUSTOM_UNAVAILABLE | CUSTOM_EXCLUDE_FOR_LISTVIEW'),
+ ('items', 38985, 'cuFlags', 1610612736, 'Scroll of Enchant Gloves - Greater Blasting - set: CUSTOM_UNAVAILABLE | CUSTOM_EXCLUDE_FOR_LISTVIEW'),
+ ('items', 44946, 'cuFlags', 1610612736, 'Scroll of Enchant Weapon - Titanguard - set: CUSTOM_UNAVAILABLE | CUSTOM_EXCLUDE_FOR_LISTVIEW'),
+ ('items', 44945, 'cuFlags', 1610612736, 'Formula: Enchant Weapon - Titanguard - set: CUSTOM_UNAVAILABLE | CUSTOM_EXCLUDE_FOR_LISTVIEW'),
+ ('spell', 30549, 'cuFlags', 1610612736, 'Engineering: Critter Enlarger - set: CUSTOM_UNAVAILABLE | CUSTOM_EXCLUDE_FOR_LISTVIEW'),
+ ('spell', 67790, 'cuFlags', 1610612736, 'Engineering: Dimensional Folder: K3 - set: CUSTOM_UNAVAILABLE | CUSTOM_EXCLUDE_FOR_LISTVIEW'),
+ ('spell', 30343, 'cuFlags', 1610612736, 'Engineering: Blue Smoke Flare - set: CUSTOM_UNAVAILABLE | CUSTOM_EXCLUDE_FOR_LISTVIEW'),
+ ('spell', 30342, 'cuFlags', 1610612736, 'Engineering: Red Smoke Flare - set: CUSTOM_UNAVAILABLE | CUSTOM_EXCLUDE_FOR_LISTVIEW'),
+ ('spell', 30561, 'cuFlags', 1610612736, 'Engineering: Goblin Tonk Controller - set: CUSTOM_UNAVAILABLE | CUSTOM_EXCLUDE_FOR_LISTVIEW'),
+ ('spell', 30573, 'cuFlags', 1610612736, 'Engineering: Gnomish Tonk Controller - set: CUSTOM_UNAVAILABLE | CUSTOM_EXCLUDE_FOR_LISTVIEW'),
+ ('spell', 12722, 'cuFlags', 1610612736, 'Engineering: Goblin Radio - set: CUSTOM_UNAVAILABLE | CUSTOM_EXCLUDE_FOR_LISTVIEW'),
+ ('spell', 12904, 'cuFlags', 1610612736, 'Engineering: Gnomish Ham Radio - set: CUSTOM_UNAVAILABLE | CUSTOM_EXCLUDE_FOR_LISTVIEW'),
+ ('spell', 12720, 'cuFlags', 1610612736, 'Engineering: Goblin "Boom" Box - set: CUSTOM_UNAVAILABLE | CUSTOM_EXCLUDE_FOR_LISTVIEW'),
+ ('spell', 12900, 'cuFlags', 1610612736, 'Engineering: Mobile Alarm- set: CUSTOM_UNAVAILABLE | CUSTOM_EXCLUDE_FOR_LISTVIEW'),
+ ('items', 23882, 'cuFlags', 1610612736, 'Schematic: Critter Enlarger - set: CUSTOM_UNAVAILABLE | CUSTOM_EXCLUDE_FOR_LISTVIEW'),
+ ('items', 23820, 'cuFlags', 1610612736, 'Critter Enlarger - set: CUSTOM_UNAVAILABLE | CUSTOM_EXCLUDE_FOR_LISTVIEW'),
+ ('items', 48933, 'cuFlags', 1610612736, 'Dimensional Folder: K3 - set: CUSTOM_UNAVAILABLE | CUSTOM_EXCLUDE_FOR_LISTVIEW'),
+ ('items', 23770, 'cuFlags', 1610612736, 'Blue Smoke Flare - set: CUSTOM_UNAVAILABLE | CUSTOM_EXCLUDE_FOR_LISTVIEW'),
+ ('items', 23769, 'cuFlags', 1610612736, 'Red Smoke Flare - set: CUSTOM_UNAVAILABLE | CUSTOM_EXCLUDE_FOR_LISTVIEW'),
+ ('items', 23831, 'cuFlags', 1610612736, 'Goblin Tonk Controller - set: CUSTOM_UNAVAILABLE | CUSTOM_EXCLUDE_FOR_LISTVIEW'),
+ ('items', 23832, 'cuFlags', 1610612736, 'Gnomish Tonk Controller - set: CUSTOM_UNAVAILABLE | CUSTOM_EXCLUDE_FOR_LISTVIEW'),
+ ('items', 10585, 'cuFlags', 1610612736, 'Goblin Radio - set: CUSTOM_UNAVAILABLE | CUSTOM_EXCLUDE_FOR_LISTVIEW'),
+ ('items', 10723, 'cuFlags', 1610612736, 'Gnomish Ham Radio - set: CUSTOM_UNAVAILABLE | CUSTOM_EXCLUDE_FOR_LISTVIEW'),
+ ('items', 10580, 'cuFlags', 1610612736, 'Goblin "Boom" Box - set: CUSTOM_UNAVAILABLE | CUSTOM_EXCLUDE_FOR_LISTVIEW'),
+ ('items', 10719, 'cuFlags', 1610612736, 'Mobile Alarm - set: CUSTOM_UNAVAILABLE | CUSTOM_EXCLUDE_FOR_LISTVIEW'),
+ ('spell', 8387, 'cuFlags', 1610612736, 'Herbalism: Find Herbs - set: CUSTOM_UNAVAILABLE | CUSTOM_EXCLUDE_FOR_LISTVIEW'),
+ ('spell', 2369, 'cuFlags', 1610612736, 'Herbalism: Herb Gathering - set: CUSTOM_UNAVAILABLE | CUSTOM_EXCLUDE_FOR_LISTVIEW'),
+ ('spell', 2371, 'cuFlags', 1610612736, 'Herbalism: Herb Gathering - set: CUSTOM_UNAVAILABLE | CUSTOM_EXCLUDE_FOR_LISTVIEW'),
+ ('spell', 52175, 'cuFlags', 1610612736, 'Inscription: Decipher - set: CUSTOM_UNAVAILABLE | CUSTOM_EXCLUDE_FOR_LISTVIEW'),
+ ('spell', 25614, 'cuFlags', 1610612736, 'Jewelcrafting: Silver Rose Pendant - set: CUSTOM_UNAVAILABLE | CUSTOM_EXCLUDE_FOR_LISTVIEW'),
+ ('spell', 32810, 'cuFlags', 1610612736, 'Jewelcrafting: Primal Stone Statue - set: CUSTOM_UNAVAILABLE | CUSTOM_EXCLUDE_FOR_LISTVIEW'),
+ ('spell', 26918, 'cuFlags', 1610612736, 'Jewelcrafting: Arcanite Sword Pendant - set: CUSTOM_UNAVAILABLE | CUSTOM_EXCLUDE_FOR_LISTVIEW'),
+ ('spell', 26920, 'cuFlags', 1610612736, 'Jewelcrafting: Blood Crown - set: CUSTOM_UNAVAILABLE | CUSTOM_EXCLUDE_FOR_LISTVIEW'),
+ ('items', 20956, 'cuFlags', 1610612736, 'Silver Rose Pendant - set: CUSTOM_UNAVAILABLE | CUSTOM_EXCLUDE_FOR_LISTVIEW'),
+ ('items', 25884, 'cuFlags', 1610612736, 'Primal Stone Statue - set: CUSTOM_UNAVAILABLE | CUSTOM_EXCLUDE_FOR_LISTVIEW'),
+ ('items', 21793, 'cuFlags', 1610612736, 'Arcanite Sword Pendant - set: CUSTOM_UNAVAILABLE | CUSTOM_EXCLUDE_FOR_LISTVIEW'),
+ ('items', 21780, 'cuFlags', 1610612736, 'Blood Crown - set: CUSTOM_UNAVAILABLE | CUSTOM_EXCLUDE_FOR_LISTVIEW'),
+ ('spell', 10550, 'cuFlags', 1610612736, 'Leatherworking: Nightscape Cloak - set: CUSTOM_UNAVAILABLE | CUSTOM_EXCLUDE_FOR_LISTVIEW'),
+ ('spell', 28224, 'cuFlags', 536870912, 'Leatherworking: Icy Scale Bracers - set: CUSTOM_UNAVAILABLE'),
+ ('spell', 28222, 'cuFlags', 536870912, 'Leatherworking: Icy Scale Breastplate - set: CUSTOM_UNAVAILABLE'),
+ ('spell', 28223, 'cuFlags', 536870912, 'Leatherworking: Icy Scale Gauntlets - set: CUSTOM_UNAVAILABLE'),
+ ('spell', 28221, 'cuFlags', 536870912, 'Leatherworking: Polar Bracers - set: CUSTOM_UNAVAILABLE'),
+ ('spell', 28220, 'cuFlags', 536870912, 'Leatherworking: Polar Gloves - set: CUSTOM_UNAVAILABLE'),
+ ('spell', 28219, 'cuFlags', 536870912, 'Leatherworking: Polar Tunic - set: CUSTOM_UNAVAILABLE'),
+ ('spell', 55243, 'cuFlags', 1610612736, 'Leatherworking: Bracers of Deflection - set: CUSTOM_UNAVAILABLE | CUSTOM_EXCLUDE_FOR_LISTVIEW'),
+ ('spell', 19106, 'cuFlags', 536870912, 'Leatherworking: Onyxia Scale Breastplate - set: CUSTOM_UNAVAILABLE'),
+ ('items', 8195, 'cuFlags', 1610612736, 'Nightscape Cloak - set: CUSTOM_UNAVAILABLE | CUSTOM_EXCLUDE_FOR_LISTVIEW'),
+ ('items', 22665, 'cuFlags', 536870912, 'Icy Scale Bracers - set: CUSTOM_UNAVAILABLE'),
+ ('items', 22664, 'cuFlags', 536870912, 'Icy Scale Breastplate - set: CUSTOM_UNAVAILABLE'),
+ ('items', 22666, 'cuFlags', 536870912, 'Icy Scale Gauntlets - set: CUSTOM_UNAVAILABLE'),
+ ('items', 22663, 'cuFlags', 536870912, 'Polar Bracers - set: CUSTOM_UNAVAILABLE'),
+ ('items', 22662, 'cuFlags', 536870912, 'Polar Gloves - set: CUSTOM_UNAVAILABLE'),
+ ('items', 22661, 'cuFlags', 536870912, 'Polar Tunic - set: CUSTOM_UNAVAILABLE'),
+ ('items', 41264, 'cuFlags', 1610612736, 'Bracers of Deflection - set: CUSTOM_UNAVAILABLE | CUSTOM_EXCLUDE_FOR_LISTVIEW'),
+ ('items', 15141, 'cuFlags', 536870912, 'Onyxia Scale Breastplate - set: CUSTOM_UNAVAILABLE'),
+ ('spell', 8388, 'cuFlags', 1610612736, 'Mining: Find Minerals - set: CUSTOM_UNAVAILABLE | CUSTOM_EXCLUDE_FOR_LISTVIEW'),
+ ('spell', 7636, 'cuFlags', 1610612736, 'Tailoring: Green Woolen Robe - set: CUSTOM_UNAVAILABLE | CUSTOM_EXCLUDE_FOR_LISTVIEW'),
+ ('spell', 8778, 'cuFlags', 1610612736, 'Tailoring: Boots of Darkness - set: CUSTOM_UNAVAILABLE | CUSTOM_EXCLUDE_FOR_LISTVIEW'),
+ ('spell', 12063, 'cuFlags', 1610612736, 'Tailoring: Stormcloth Gloves - set: CUSTOM_UNAVAILABLE | CUSTOM_EXCLUDE_FOR_LISTVIEW'),
+ ('spell', 12062, 'cuFlags', 1610612736, 'Tailoring: Stormcloth Pants - set: CUSTOM_UNAVAILABLE | CUSTOM_EXCLUDE_FOR_LISTVIEW'),
+ ('spell', 12068, 'cuFlags', 1610612736, 'Tailoring: Stormcloth Vest - set: CUSTOM_UNAVAILABLE | CUSTOM_EXCLUDE_FOR_LISTVIEW'),
+ ('spell', 12083, 'cuFlags', 1610612736, 'Tailoring: Stormcloth Headband - set: CUSTOM_UNAVAILABLE | CUSTOM_EXCLUDE_FOR_LISTVIEW'),
+ ('spell', 12087, 'cuFlags', 1610612736, 'Tailoring: Stormcloth Shoulders - set: CUSTOM_UNAVAILABLE | CUSTOM_EXCLUDE_FOR_LISTVIEW'),
+ ('spell', 12090, 'cuFlags', 1610612736, 'Tailoring: Stormcloth Boots - set: CUSTOM_UNAVAILABLE | CUSTOM_EXCLUDE_FOR_LISTVIEW'),
+ ('spell', 28208, 'cuFlags', 536870912, 'Tailoring: Glacial Cloak - set: CUSTOM_UNAVAILABLE'),
+ ('spell', 28205, 'cuFlags', 536870912, 'Tailoring: Glacial Gloves - set: CUSTOM_UNAVAILABLE'),
+ ('spell', 28207, 'cuFlags', 536870912, 'Tailoring: Glacial Vest - set: CUSTOM_UNAVAILABLE'),
+ ('spell', 28209, 'cuFlags', 536870912, 'Tailoring: Glacial Wrists - set: CUSTOM_UNAVAILABLE'),
+ ('spell', 36670, 'cuFlags', 1610612736, 'Tailoring: Lifeblood Belt - set: CUSTOM_UNAVAILABLE | CUSTOM_EXCLUDE_FOR_LISTVIEW'),
+ ('spell', 36672, 'cuFlags', 1610612736, 'Tailoring: Lifeblood Bracers - set: CUSTOM_UNAVAILABLE | CUSTOM_EXCLUDE_FOR_LISTVIEW'),
+ ('spell', 36669, 'cuFlags', 1610612736, 'Tailoring: Lifeblood Leggings - set: CUSTOM_UNAVAILABLE | CUSTOM_EXCLUDE_FOR_LISTVIEW'),
+ ('spell', 36667, 'cuFlags', 1610612736, 'Tailoring: Netherflame Belt - set: CUSTOM_UNAVAILABLE | CUSTOM_EXCLUDE_FOR_LISTVIEW'),
+ ('spell', 36668, 'cuFlags', 1610612736, 'Tailoring: Netherflame Boots - set: CUSTOM_UNAVAILABLE | CUSTOM_EXCLUDE_FOR_LISTVIEW'),
+ ('spell', 36665, 'cuFlags', 1610612736, 'Tailoring: Netherflame Robe - set: CUSTOM_UNAVAILABLE | CUSTOM_EXCLUDE_FOR_LISTVIEW'),
+ ('spell', 56048, 'cuFlags', 1610612736, 'Tailoring: Duskweave Boots - set: CUSTOM_UNAVAILABLE | CUSTOM_EXCLUDE_FOR_LISTVIEW'),
+ ('spell', 31461, 'cuFlags', 1610612736, 'Tailoring: Heavy Netherweave Net - set: CUSTOM_UNAVAILABLE | CUSTOM_EXCLUDE_FOR_LISTVIEW'),
+ ('items', 6243, 'cuFlags', 1610612736, 'Green Woolen Robe - set: CUSTOM_UNAVAILABLE | CUSTOM_EXCLUDE_FOR_LISTVIEW'),
+ ('items', 7027, 'cuFlags', 1610612736, 'Boots of Darkness - set: CUSTOM_UNAVAILABLE | CUSTOM_EXCLUDE_FOR_LISTVIEW'),
+ ('items', 10011, 'cuFlags', 1610612736, 'Stormcloth Gloves - set: CUSTOM_UNAVAILABLE | CUSTOM_EXCLUDE_FOR_LISTVIEW'),
+ ('items', 10010, 'cuFlags', 1610612736, 'Stormcloth Pants - set: CUSTOM_UNAVAILABLE | CUSTOM_EXCLUDE_FOR_LISTVIEW'),
+ ('items', 10020, 'cuFlags', 1610612736, 'Stormcloth Vest - set: CUSTOM_UNAVAILABLE | CUSTOM_EXCLUDE_FOR_LISTVIEW'),
+ ('items', 10032, 'cuFlags', 1610612736, 'Stormcloth Headband - set: CUSTOM_UNAVAILABLE | CUSTOM_EXCLUDE_FOR_LISTVIEW'),
+ ('items', 10038, 'cuFlags', 1610612736, 'Stormcloth Shoulders - set: CUSTOM_UNAVAILABLE | CUSTOM_EXCLUDE_FOR_LISTVIEW'),
+ ('items', 10039, 'cuFlags', 1610612736, 'Stormcloth Boots - set: CUSTOM_UNAVAILABLE | CUSTOM_EXCLUDE_FOR_LISTVIEW'),
+ ('items', 22658, 'cuFlags', 536870912, 'Glacial Cloak - set: CUSTOM_UNAVAILABLE'),
+ ('items', 22654, 'cuFlags', 536870912, 'Glacial Gloves - set: CUSTOM_UNAVAILABLE'),
+ ('items', 22652, 'cuFlags', 536870912, 'Glacial Vest - set: CUSTOM_UNAVAILABLE'),
+ ('items', 22655, 'cuFlags', 536870912, 'Glacial Wrists - set: CUSTOM_UNAVAILABLE'),
+ ('items', 30463, 'cuFlags', 1610612736, 'Lifeblood Belt - set: CUSTOM_UNAVAILABLE | CUSTOM_EXCLUDE_FOR_LISTVIEW'),
+ ('items', 30464, 'cuFlags', 1610612736, 'Lifeblood Bracers - set: CUSTOM_UNAVAILABLE | CUSTOM_EXCLUDE_FOR_LISTVIEW'),
+ ('items', 30465, 'cuFlags', 1610612736, 'Lifeblood Leggings - set: CUSTOM_UNAVAILABLE | CUSTOM_EXCLUDE_FOR_LISTVIEW'),
+ ('items', 30460, 'cuFlags', 1610612736, 'Netherflame Belt - set: CUSTOM_UNAVAILABLE | CUSTOM_EXCLUDE_FOR_LISTVIEW'),
+ ('items', 30461, 'cuFlags', 1610612736, 'Netherflame Boots - set: CUSTOM_UNAVAILABLE | CUSTOM_EXCLUDE_FOR_LISTVIEW'),
+ ('items', 30459, 'cuFlags', 1610612736, 'Netherflame Robe - set: CUSTOM_UNAVAILABLE | CUSTOM_EXCLUDE_FOR_LISTVIEW'),
+ ('items', 41544, 'cuFlags', 1610612736, 'Duskweave Boots - set: CUSTOM_UNAVAILABLE | CUSTOM_EXCLUDE_FOR_LISTVIEW'),
+ ('items', 24269, 'cuFlags', 1610612736, 'Heavy Netherweave Net - set: CUSTOM_UNAVAILABLE | CUSTOM_EXCLUDE_FOR_LISTVIEW');
+
+UPDATE `aowow_dbversion` SET
+ `sql` = CONCAT(IFNULL(`sql`, ''), ' spell items'),
+ `build` = CONCAT(IFNULL(`build`, ''), ' profiler');
diff --git a/setup/sql/updates/1774728772_01.sql b/setup/sql/updates/1774728772_01.sql
new file mode 100644
index 00000000..f30e8121
--- /dev/null
+++ b/setup/sql/updates/1774728772_01.sql
@@ -0,0 +1 @@
+UPDATE `aowow_dbversion` SET `sql` = CONCAT(IFNULL(`sql`, ''), ' search');
diff --git a/setup/sql/updates/1775758635_01.sql b/setup/sql/updates/1775758635_01.sql
new file mode 100644
index 00000000..6d163b57
--- /dev/null
+++ b/setup/sql/updates/1775758635_01.sql
@@ -0,0 +1 @@
+UPDATE `aowow_dbversion` SET `build` = CONCAT(IFNULL(`build`, ''), ' profiler');
diff --git a/setup/updates/1433023200_01.sql b/setup/sql/updates/v1.0/1433023200_01.sql
similarity index 100%
rename from setup/updates/1433023200_01.sql
rename to setup/sql/updates/v1.0/1433023200_01.sql
diff --git a/setup/updates/1433023200_02.sql b/setup/sql/updates/v1.0/1433023200_02.sql
similarity index 100%
rename from setup/updates/1433023200_02.sql
rename to setup/sql/updates/v1.0/1433023200_02.sql
diff --git a/setup/updates/1433793600_01.sql b/setup/sql/updates/v1.0/1433793600_01.sql
similarity index 100%
rename from setup/updates/1433793600_01.sql
rename to setup/sql/updates/v1.0/1433793600_01.sql
diff --git a/setup/updates/1435777200_01.sql b/setup/sql/updates/v1.1/1435777200_01.sql
similarity index 100%
rename from setup/updates/1435777200_01.sql
rename to setup/sql/updates/v1.1/1435777200_01.sql
diff --git a/setup/updates/1436392800_01.sql b/setup/sql/updates/v1.1/1436392800_01.sql
similarity index 100%
rename from setup/updates/1436392800_01.sql
rename to setup/sql/updates/v1.1/1436392800_01.sql
diff --git a/setup/updates/1436619600_01.sql b/setup/sql/updates/v1.1/1436619600_01.sql
similarity index 100%
rename from setup/updates/1436619600_01.sql
rename to setup/sql/updates/v1.1/1436619600_01.sql
diff --git a/setup/updates/1436634000_01.sql b/setup/sql/updates/v1.1/1436634000_01.sql
similarity index 100%
rename from setup/updates/1436634000_01.sql
rename to setup/sql/updates/v1.1/1436634000_01.sql
diff --git a/setup/updates/1436634000_02.sql b/setup/sql/updates/v1.1/1436634000_02.sql
similarity index 100%
rename from setup/updates/1436634000_02.sql
rename to setup/sql/updates/v1.1/1436634000_02.sql
diff --git a/setup/updates/1436735830_01.sql b/setup/sql/updates/v1.1/1436735830_01.sql
similarity index 100%
rename from setup/updates/1436735830_01.sql
rename to setup/sql/updates/v1.1/1436735830_01.sql
diff --git a/setup/updates/1436739821_01.sql b/setup/sql/updates/v1.1/1436739821_01.sql
similarity index 100%
rename from setup/updates/1436739821_01.sql
rename to setup/sql/updates/v1.1/1436739821_01.sql
diff --git a/setup/updates/1436903207_01.sql b/setup/sql/updates/v1.1/1436903207_01.sql
similarity index 100%
rename from setup/updates/1436903207_01.sql
rename to setup/sql/updates/v1.1/1436903207_01.sql
diff --git a/setup/updates/1437329787_01.sql b/setup/sql/updates/v1.1/1437329787_01.sql
similarity index 100%
rename from setup/updates/1437329787_01.sql
rename to setup/sql/updates/v1.1/1437329787_01.sql
diff --git a/setup/updates/1437430574_01.sql b/setup/sql/updates/v1.1/1437430574_01.sql
similarity index 100%
rename from setup/updates/1437430574_01.sql
rename to setup/sql/updates/v1.1/1437430574_01.sql
diff --git a/setup/updates/1437472069_01.sql b/setup/sql/updates/v1.1/1437472069_01.sql
similarity index 100%
rename from setup/updates/1437472069_01.sql
rename to setup/sql/updates/v1.1/1437472069_01.sql
diff --git a/setup/updates/1438620486_01.sql b/setup/sql/updates/v1.1/1438620486_01.sql
similarity index 100%
rename from setup/updates/1438620486_01.sql
rename to setup/sql/updates/v1.1/1438620486_01.sql
diff --git a/setup/updates/1438715648_01.sql b/setup/sql/updates/v1.1/1438715648_01.sql
similarity index 100%
rename from setup/updates/1438715648_01.sql
rename to setup/sql/updates/v1.1/1438715648_01.sql
diff --git a/setup/updates/1438878038_01.sql b/setup/sql/updates/v1.1/1438878038_01.sql
similarity index 100%
rename from setup/updates/1438878038_01.sql
rename to setup/sql/updates/v1.1/1438878038_01.sql
diff --git a/setup/updates/1438970456_01.sql b/setup/sql/updates/v1.1/1438970456_01.sql
similarity index 100%
rename from setup/updates/1438970456_01.sql
rename to setup/sql/updates/v1.1/1438970456_01.sql
diff --git a/setup/updates/1439297934_01.sql b/setup/sql/updates/v1.1/1439297934_01.sql
similarity index 100%
rename from setup/updates/1439297934_01.sql
rename to setup/sql/updates/v1.1/1439297934_01.sql
diff --git a/setup/updates/1439469082_01.sql b/setup/sql/updates/v1.1/1439469082_01.sql
similarity index 100%
rename from setup/updates/1439469082_01.sql
rename to setup/sql/updates/v1.1/1439469082_01.sql
diff --git a/setup/updates/1439590146_01.sql b/setup/sql/updates/v1.1/1439590146_01.sql
similarity index 100%
rename from setup/updates/1439590146_01.sql
rename to setup/sql/updates/v1.1/1439590146_01.sql
diff --git a/setup/updates/1439840492_01.sql b/setup/sql/updates/v1.1/1439840492_01.sql
similarity index 100%
rename from setup/updates/1439840492_01.sql
rename to setup/sql/updates/v1.1/1439840492_01.sql
diff --git a/setup/updates/1439909965_01.sql b/setup/sql/updates/v1.1/1439909965_01.sql
similarity index 100%
rename from setup/updates/1439909965_01.sql
rename to setup/sql/updates/v1.1/1439909965_01.sql
diff --git a/setup/updates/1439924313_01.sql b/setup/sql/updates/v1.1/1439924313_01.sql
similarity index 100%
rename from setup/updates/1439924313_01.sql
rename to setup/sql/updates/v1.1/1439924313_01.sql
diff --git a/setup/updates/1445293761_01.sql b/setup/sql/updates/v1.1/1445293761_01.sql
similarity index 100%
rename from setup/updates/1445293761_01.sql
rename to setup/sql/updates/v1.1/1445293761_01.sql
diff --git a/setup/updates/1446293928_01.sql b/setup/sql/updates/v1.1/1446293928_01.sql
similarity index 100%
rename from setup/updates/1446293928_01.sql
rename to setup/sql/updates/v1.1/1446293928_01.sql
diff --git a/setup/updates/1446917082_01.sql b/setup/sql/updates/v1.1/1446917082_01.sql
similarity index 100%
rename from setup/updates/1446917082_01.sql
rename to setup/sql/updates/v1.1/1446917082_01.sql
diff --git a/setup/updates/1448204650_01.sql b/setup/sql/updates/v1.1/1448204650_01.sql
similarity index 100%
rename from setup/updates/1448204650_01.sql
rename to setup/sql/updates/v1.1/1448204650_01.sql
diff --git a/setup/updates/1448727750_01.sql b/setup/sql/updates/v1.1/1448727750_01.sql
similarity index 100%
rename from setup/updates/1448727750_01.sql
rename to setup/sql/updates/v1.1/1448727750_01.sql
diff --git a/setup/updates/1452718627_01.sql b/setup/sql/updates/v1.1/1452718627_01.sql
similarity index 100%
rename from setup/updates/1452718627_01.sql
rename to setup/sql/updates/v1.1/1452718627_01.sql
diff --git a/setup/updates/1456585695_01.sql b/setup/sql/updates/v1.1/1456585695_01.sql
similarity index 100%
rename from setup/updates/1456585695_01.sql
rename to setup/sql/updates/v1.1/1456585695_01.sql
diff --git a/setup/updates/1484926142_01.sql b/setup/sql/updates/v1.1/1484926142_01.sql
similarity index 100%
rename from setup/updates/1484926142_01.sql
rename to setup/sql/updates/v1.1/1484926142_01.sql
diff --git a/setup/updates/1486948902_01.sql b/setup/sql/updates/v1.1/1486948902_01.sql
similarity index 100%
rename from setup/updates/1486948902_01.sql
rename to setup/sql/updates/v1.1/1486948902_01.sql
diff --git a/setup/updates/1487858459_01.sql b/setup/sql/updates/v1.1/1487858459_01.sql
similarity index 100%
rename from setup/updates/1487858459_01.sql
rename to setup/sql/updates/v1.1/1487858459_01.sql
diff --git a/setup/updates/1488061468_01.sql b/setup/sql/updates/v1.1/1488061468_01.sql
similarity index 100%
rename from setup/updates/1488061468_01.sql
rename to setup/sql/updates/v1.1/1488061468_01.sql
diff --git a/setup/updates/1488745158_01.sql b/setup/sql/updates/v1.1/1488745158_01.sql
similarity index 100%
rename from setup/updates/1488745158_01.sql
rename to setup/sql/updates/v1.1/1488745158_01.sql
diff --git a/setup/updates/1488745158_02.sql b/setup/sql/updates/v1.1/1488745158_02.sql
similarity index 100%
rename from setup/updates/1488745158_02.sql
rename to setup/sql/updates/v1.1/1488745158_02.sql
diff --git a/setup/updates/1489291710_01.sql b/setup/sql/updates/v1.1/1489291710_01.sql
similarity index 100%
rename from setup/updates/1489291710_01.sql
rename to setup/sql/updates/v1.1/1489291710_01.sql
diff --git a/setup/updates/1489673970_01.sql b/setup/sql/updates/v1.1/1489673970_01.sql
similarity index 100%
rename from setup/updates/1489673970_01.sql
rename to setup/sql/updates/v1.1/1489673970_01.sql
diff --git a/setup/updates/1489942886_01.sql b/setup/sql/updates/v1.1/1489942886_01.sql
similarity index 100%
rename from setup/updates/1489942886_01.sql
rename to setup/sql/updates/v1.1/1489942886_01.sql
diff --git a/setup/updates/1489942886_02.sql b/setup/sql/updates/v1.1/1489942886_02.sql
similarity index 100%
rename from setup/updates/1489942886_02.sql
rename to setup/sql/updates/v1.1/1489942886_02.sql
diff --git a/setup/updates/1489942886_03.sql b/setup/sql/updates/v1.1/1489942886_03.sql
similarity index 100%
rename from setup/updates/1489942886_03.sql
rename to setup/sql/updates/v1.1/1489942886_03.sql
diff --git a/setup/updates/1489964225_01.sql b/setup/sql/updates/v1.1/1489964225_01.sql
similarity index 100%
rename from setup/updates/1489964225_01.sql
rename to setup/sql/updates/v1.1/1489964225_01.sql
diff --git a/setup/updates/1490028370_01.sql b/setup/sql/updates/v1.1/1490028370_01.sql
similarity index 100%
rename from setup/updates/1490028370_01.sql
rename to setup/sql/updates/v1.1/1490028370_01.sql
diff --git a/setup/updates/1490049258_01.sql b/setup/sql/updates/v1.1/1490049258_01.sql
similarity index 100%
rename from setup/updates/1490049258_01.sql
rename to setup/sql/updates/v1.1/1490049258_01.sql
diff --git a/setup/updates/1490789225_01.sql b/setup/sql/updates/v1.1/1490789225_01.sql
similarity index 100%
rename from setup/updates/1490789225_01.sql
rename to setup/sql/updates/v1.1/1490789225_01.sql
diff --git a/setup/updates/1490815301_01.sql b/setup/sql/updates/v1.1/1490815301_01.sql
similarity index 100%
rename from setup/updates/1490815301_01.sql
rename to setup/sql/updates/v1.1/1490815301_01.sql
diff --git a/setup/updates/1490912249_01.sql b/setup/sql/updates/v1.1/1490912249_01.sql
similarity index 100%
rename from setup/updates/1490912249_01.sql
rename to setup/sql/updates/v1.1/1490912249_01.sql
diff --git a/setup/updates/1491915058_01.sql b/setup/sql/updates/v1.1/1491915058_01.sql
similarity index 100%
rename from setup/updates/1491915058_01.sql
rename to setup/sql/updates/v1.1/1491915058_01.sql
diff --git a/setup/updates/1491915872_01.sql b/setup/sql/updates/v1.1/1491915872_01.sql
similarity index 100%
rename from setup/updates/1491915872_01.sql
rename to setup/sql/updates/v1.1/1491915872_01.sql
diff --git a/setup/updates/1493755253_01.sql b/setup/sql/updates/v1.1/1493755253_01.sql
similarity index 100%
rename from setup/updates/1493755253_01.sql
rename to setup/sql/updates/v1.1/1493755253_01.sql
diff --git a/setup/updates/1493756026_01.sql b/setup/sql/updates/v1.1/1493756026_01.sql
similarity index 100%
rename from setup/updates/1493756026_01.sql
rename to setup/sql/updates/v1.1/1493756026_01.sql
diff --git a/setup/updates/1493857753_01.sql b/setup/sql/updates/v1.1/1493857753_01.sql
similarity index 100%
rename from setup/updates/1493857753_01.sql
rename to setup/sql/updates/v1.1/1493857753_01.sql
diff --git a/setup/updates/1493924184_01.sql b/setup/sql/updates/v1.1/1493924184_01.sql
similarity index 100%
rename from setup/updates/1493924184_01.sql
rename to setup/sql/updates/v1.1/1493924184_01.sql
diff --git a/setup/updates/1494853933_01.sql b/setup/sql/updates/v1.1/1494853933_01.sql
similarity index 100%
rename from setup/updates/1494853933_01.sql
rename to setup/sql/updates/v1.1/1494853933_01.sql
diff --git a/setup/updates/1504448040_01.sql b/setup/sql/updates/v1.1/1504448040_01.sql
similarity index 100%
rename from setup/updates/1504448040_01.sql
rename to setup/sql/updates/v1.1/1504448040_01.sql
diff --git a/setup/updates/1521735363_01.sql b/setup/sql/updates/v1.1/1521735363_01.sql
similarity index 100%
rename from setup/updates/1521735363_01.sql
rename to setup/sql/updates/v1.1/1521735363_01.sql
diff --git a/setup/updates/1521735363_02.sql b/setup/sql/updates/v1.1/1521735363_02.sql
similarity index 100%
rename from setup/updates/1521735363_02.sql
rename to setup/sql/updates/v1.1/1521735363_02.sql
diff --git a/setup/updates/1522146994_01.sql b/setup/sql/updates/v1.2/1522146994_01.sql
similarity index 100%
rename from setup/updates/1522146994_01.sql
rename to setup/sql/updates/v1.2/1522146994_01.sql
diff --git a/setup/updates/1522230798_01.sql b/setup/sql/updates/v1.2/1522230798_01.sql
similarity index 100%
rename from setup/updates/1522230798_01.sql
rename to setup/sql/updates/v1.2/1522230798_01.sql
diff --git a/setup/updates/1522321542_01.sql b/setup/sql/updates/v1.2/1522321542_01.sql
similarity index 100%
rename from setup/updates/1522321542_01.sql
rename to setup/sql/updates/v1.2/1522321542_01.sql
diff --git a/setup/updates/1522421324_01.sql b/setup/sql/updates/v1.2/1522421324_01.sql
similarity index 100%
rename from setup/updates/1522421324_01.sql
rename to setup/sql/updates/v1.2/1522421324_01.sql
diff --git a/setup/updates/1522499480_01.sql b/setup/sql/updates/v1.2/1522499480_01.sql
similarity index 100%
rename from setup/updates/1522499480_01.sql
rename to setup/sql/updates/v1.2/1522499480_01.sql
diff --git a/setup/updates/1522673571_01.sql b/setup/sql/updates/v1.2/1522673571_01.sql
similarity index 100%
rename from setup/updates/1522673571_01.sql
rename to setup/sql/updates/v1.2/1522673571_01.sql
diff --git a/setup/updates/1524389034_01.sql b/setup/sql/updates/v1.2/1524389034_01.sql
similarity index 100%
rename from setup/updates/1524389034_01.sql
rename to setup/sql/updates/v1.2/1524389034_01.sql
diff --git a/setup/updates/1524905453_01.sql b/setup/sql/updates/v1.2/1524905453_01.sql
similarity index 100%
rename from setup/updates/1524905453_01.sql
rename to setup/sql/updates/v1.2/1524905453_01.sql
diff --git a/setup/updates/1524907872_01.sql b/setup/sql/updates/v1.2/1524907872_01.sql
similarity index 100%
rename from setup/updates/1524907872_01.sql
rename to setup/sql/updates/v1.2/1524907872_01.sql
diff --git a/setup/updates/1525094404_01.sql b/setup/sql/updates/v1.2/1525094404_01.sql
similarity index 100%
rename from setup/updates/1525094404_01.sql
rename to setup/sql/updates/v1.2/1525094404_01.sql
diff --git a/setup/updates/1525726941_01.sql b/setup/sql/updates/v1.2/1525726941_01.sql
similarity index 100%
rename from setup/updates/1525726941_01.sql
rename to setup/sql/updates/v1.2/1525726941_01.sql
diff --git a/setup/updates/1525726941_02.sql b/setup/sql/updates/v1.2/1525726941_02.sql
similarity index 100%
rename from setup/updates/1525726941_02.sql
rename to setup/sql/updates/v1.2/1525726941_02.sql
diff --git a/setup/updates/1527333495_01.sql b/setup/sql/updates/v1.2/1527333495_01.sql
similarity index 100%
rename from setup/updates/1527333495_01.sql
rename to setup/sql/updates/v1.2/1527333495_01.sql
diff --git a/setup/updates/1527343032_01.sql b/setup/sql/updates/v1.2/1527343032_01.sql
similarity index 100%
rename from setup/updates/1527343032_01.sql
rename to setup/sql/updates/v1.2/1527343032_01.sql
diff --git a/setup/updates/1528316365_01.sql b/setup/sql/updates/v1.2/1528316365_01.sql
similarity index 100%
rename from setup/updates/1528316365_01.sql
rename to setup/sql/updates/v1.2/1528316365_01.sql
diff --git a/setup/updates/1531668311_01.sql b/setup/sql/updates/v1.2/1531668311_01.sql
similarity index 100%
rename from setup/updates/1531668311_01.sql
rename to setup/sql/updates/v1.2/1531668311_01.sql
diff --git a/setup/sql/updates/v1.2/1543271379_01.sql b/setup/sql/updates/v1.2/1543271379_01.sql
new file mode 100644
index 00000000..6d163b57
--- /dev/null
+++ b/setup/sql/updates/v1.2/1543271379_01.sql
@@ -0,0 +1 @@
+UPDATE `aowow_dbversion` SET `build` = CONCAT(IFNULL(`build`, ''), ' profiler');
diff --git a/setup/updates/1543774778_01.sql b/setup/sql/updates/v1.2/1543774778_01.sql
similarity index 100%
rename from setup/updates/1543774778_01.sql
rename to setup/sql/updates/v1.2/1543774778_01.sql
diff --git a/setup/updates/1544826311_01.sql b/setup/sql/updates/v1.2/1544826311_01.sql
similarity index 100%
rename from setup/updates/1544826311_01.sql
rename to setup/sql/updates/v1.2/1544826311_01.sql
diff --git a/setup/updates/1581549222_01.sql b/setup/sql/updates/v1.2/1581549222_01.sql
similarity index 100%
rename from setup/updates/1581549222_01.sql
rename to setup/sql/updates/v1.2/1581549222_01.sql
diff --git a/setup/updates/1582485391_01.sql b/setup/sql/updates/v1.2/1582485391_01.sql
similarity index 100%
rename from setup/updates/1582485391_01.sql
rename to setup/sql/updates/v1.2/1582485391_01.sql
diff --git a/setup/updates/1582486388_01.sql b/setup/sql/updates/v1.2/1582486388_01.sql
similarity index 100%
rename from setup/updates/1582486388_01.sql
rename to setup/sql/updates/v1.2/1582486388_01.sql
diff --git a/setup/updates/1586092309_01.sql b/setup/sql/updates/v1.2/1586092309_01.sql
similarity index 100%
rename from setup/updates/1586092309_01.sql
rename to setup/sql/updates/v1.2/1586092309_01.sql
diff --git a/setup/updates/1586458384_01.sql b/setup/sql/updates/v1.2/1586458384_01.sql
similarity index 100%
rename from setup/updates/1586458384_01.sql
rename to setup/sql/updates/v1.2/1586458384_01.sql
diff --git a/setup/updates/1587314471_01.sql b/setup/sql/updates/v1.2/1587314471_01.sql
similarity index 100%
rename from setup/updates/1587314471_01.sql
rename to setup/sql/updates/v1.2/1587314471_01.sql
diff --git a/setup/updates/1588517065_01.sql b/setup/sql/updates/v1.2/1588517065_01.sql
similarity index 100%
rename from setup/updates/1588517065_01.sql
rename to setup/sql/updates/v1.2/1588517065_01.sql
diff --git a/setup/updates/1590506556_01.sql b/setup/sql/updates/v1.2/1590506556_01.sql
similarity index 100%
rename from setup/updates/1590506556_01.sql
rename to setup/sql/updates/v1.2/1590506556_01.sql
diff --git a/setup/updates/1590572038_01.sql b/setup/sql/updates/v1.2/1590572038_01.sql
similarity index 100%
rename from setup/updates/1590572038_01.sql
rename to setup/sql/updates/v1.2/1590572038_01.sql
diff --git a/setup/updates/1590687329_01.sql b/setup/sql/updates/v1.2/1590687329_01.sql
similarity index 100%
rename from setup/updates/1590687329_01.sql
rename to setup/sql/updates/v1.2/1590687329_01.sql
diff --git a/setup/updates/1591223185_01.sql b/setup/sql/updates/v1.2/1591223185_01.sql
similarity index 100%
rename from setup/updates/1591223185_01.sql
rename to setup/sql/updates/v1.2/1591223185_01.sql
diff --git a/setup/updates/1591451737_01.sql b/setup/sql/updates/v1.2/1591451737_01.sql
similarity index 100%
rename from setup/updates/1591451737_01.sql
rename to setup/sql/updates/v1.2/1591451737_01.sql
diff --git a/setup/updates/1608244863_01.sql b/setup/sql/updates/v1.2/1608244863_01.sql
similarity index 100%
rename from setup/updates/1608244863_01.sql
rename to setup/sql/updates/v1.2/1608244863_01.sql
diff --git a/setup/updates/1609244309_01.sql b/setup/sql/updates/v1.2/1609244309_01.sql
similarity index 100%
rename from setup/updates/1609244309_01.sql
rename to setup/sql/updates/v1.2/1609244309_01.sql
diff --git a/setup/updates/1613670956_01.sql b/setup/sql/updates/v1.2/1613670956_01.sql
similarity index 100%
rename from setup/updates/1613670956_01.sql
rename to setup/sql/updates/v1.2/1613670956_01.sql
diff --git a/setup/updates/1645476214_01.sql b/setup/sql/updates/v1.2/1645476214_01.sql
similarity index 100%
rename from setup/updates/1645476214_01.sql
rename to setup/sql/updates/v1.2/1645476214_01.sql
diff --git a/setup/updates/1645476214_02.sql b/setup/sql/updates/v1.2/1645476214_02.sql
similarity index 100%
rename from setup/updates/1645476214_02.sql
rename to setup/sql/updates/v1.2/1645476214_02.sql
diff --git a/setup/sql/updates/v1.2/1647813287_01.sql b/setup/sql/updates/v1.2/1647813287_01.sql
new file mode 100644
index 00000000..806c663d
--- /dev/null
+++ b/setup/sql/updates/v1.2/1647813287_01.sql
@@ -0,0 +1 @@
+UPDATE `aowow_dbversion` SET `build` = CONCAT(IFNULL(`build`, ''), ' markup');
diff --git a/setup/updates/1647956309_01.sql b/setup/sql/updates/v1.2/1647956309_01.sql
similarity index 100%
rename from setup/updates/1647956309_01.sql
rename to setup/sql/updates/v1.2/1647956309_01.sql
diff --git a/setup/updates/1647956309_02.sql b/setup/sql/updates/v1.2/1647956309_02.sql
similarity index 100%
rename from setup/updates/1647956309_02.sql
rename to setup/sql/updates/v1.2/1647956309_02.sql
diff --git a/setup/tools/CLISetup.class.php b/setup/tools/CLISetup.class.php
index 0c5e9b0b..cab2a554 100644
--- a/setup/tools/CLISetup.class.php
+++ b/setup/tools/CLISetup.class.php
@@ -1,5 +1,7 @@
LOCALE_EN, 'enGB' => LOCALE_EN, 'enUS' => LOCALE_EN,
- 'frFR' => LOCALE_FR,
- 'deDE' => LOCALE_DE,
- 'zhCN' => LOCALE_CN, 'enCN' => LOCALE_CN,
- 'esES' => LOCALE_ES, 'esMX' => LOCALE_ES,
- 'ruRU' => LOCALE_RU
- );
+
+ public const SQL_BATCH = 1000; // max. n items per sql insert
public const LOCK_OFF = 0;
public const LOCK_ON = 1;
public const LOCK_RESTORE = 2;
- private static $lock = 1;
+ private static $lock = self::LOCK_ON;
- private const ARGV_REQUIRED = 0x01;
- private const ARGV_OPTIONAL = 0x02;
- private const ARGV_ARRAY = 0x10;
+ public const ARGV_NONE = 0x00;
+ public const ARGV_REQUIRED = 0x01;
+ public const ARGV_OPTIONAL = 0x02;
+ public const ARGV_PARAM = 0x04; // parameter to another argument
+ public const ARGV_ARRAY = 0x10; // arg accepts list of values
+
+ public const OPT_GRP_SETUP = 0;
+ public const OPT_GRP_UTIL = 1;
+ public const OPT_GRP_MISC = 2;
+
+ private const GLOBALSTRINGS_LUA = '%s%sinterface/framexml/globalstrings.lua';
private static $opts = [];
- private static $optGroups = ['AoWoW Setup', 'Utility Functions', 'Additional Options', 'Additional arguments specific to --build=simpleImg', 'Additional arguments specific to --build=complexImg'];
- private static $optDefs = array( // cmd => [groupId, aliasses[], flags, description, appendix]
- 'setup' => [0, ['s', 'firstrun'], 0x00, 'Step by step initial setup. Resumes if interrupted.', '' ],
- 'update' => [0, ['u'], 0x00, 'Apply new sql updates fetched from Github and run --sync as needed.', '' ],
- 'dbconfig' => [1, [], 0x00, 'Set up DB connection.', '' ],
- 'siteconfig' => [1, [], 0x00, 'Set up site variables.', '' ],
- 'account' => [1, [], 0x00, 'Create an account with admin privileges.', '' ],
- 'sql' => [1, [], 0x12, 'Generate DB content from your world tables.', '=' ],
- 'build' => [1, [], 0x12, 'Compile image files and data dumps.', '=' ],
- 'sync' => [1, [], 0x12, 'Regenerate tables/files that depend on given world DB table.', '='],
- 'dbc' => [1, [], 0x11, 'Extract dbc files from mpqDataDir into sql table. Structure must be defined in setup/dbc.class.php.', '=' ],
- 'delete' => [2, ['d'], 0x00, 'Delete dbc_* tables generated by this prompt when done.', '' ],
- 'log' => [2, [], 0x01, 'Write CLI ouput to file.', '=logfile' ],
- 'help' => [2, ['h'], 0x00, 'Display contextual help, if available.', '' ],
- 'force' => [2, ['f'], 0x00, 'Force existing files to be overwritten.', '' ],
- 'locales' => [2, [], 0x12, 'Limit setup to enUS, frFR, deDE, zhCN, esES and/or ruRU. (does not override config settings)', '=' ],
- 'mpqDataDir' => [2, [], 0x02, 'Manually point to directory with extracted mpq files. This is limited to setup/ (default: setup/mpqData/)', '=path/' ],
- 'icons' => [3, ['1'], 0x00, 'Generate icons for spells, items, classes, races, ect.', '' ],
- 'glyphs' => [3, ['2'], 0x00, 'Generate decorative glyph symbols displayed on related item and spell pages.', '' ],
- 'pagetexts' => [3, ['3'], 0x00, 'Generate images contained in text on readable items and gameobjects.', '' ],
- 'loadingscreens' => [3, ['4'], 0x00, 'Generate loading screen images (not used on page; skipped by default)', '' ],
- 'talentbgs' => [4, ['1'], 0x00, 'Generate backgrounds for the talent calculator.', '' ],
- 'maps' => [4, ['2'], 0x00, 'Generate zone and continental maps.', '' ],
- 'spawn-maps' => [4, ['3'], 0x00, 'Fallback to generate alpha masks for each zone to match creature and gameobject spawn points.', '' ],
- 'artwork' => [4, ['4'], 0x00, 'Generate images from /glues/credits (not used on page; skipped by default))', '' ],
- 'area-maps' => [4, ['5'], 0x00, 'Generate additional area maps with highlighting for subzones (optional; skipped by default)', '' ]
+ private static $optGroups = ['AoWoW Setup', 'Utility Functions', 'Additional Options'];
+ private static $optDefs = array( // cmd => [groupId, aliases[], argvFlags, description, appendix]
+ 'delete' => [self::OPT_GRP_MISC, ['d'], self::ARGV_NONE, 'Delete dbc_* tables generated by this prompt when done. (not recommended)', '' ],
+ 'log' => [self::OPT_GRP_MISC, [], self::ARGV_REQUIRED, 'Write CLI ouput to file.', '=logfile' ],
+ 'help' => [self::OPT_GRP_MISC, ['h'], self::ARGV_NONE, 'Display contextual help, if available.', '' ],
+ 'force' => [self::OPT_GRP_MISC, ['f'], self::ARGV_NONE, 'Force existing files to be overwritten.', '' ],
+ 'locales' => [self::OPT_GRP_MISC, [], self::ARGV_ARRAY | self::ARGV_OPTIONAL, 'Limit setup to enUS, frFR, deDE, zhCN, esES and/or ruRU. (does not override config settings)', '='],
+ 'datasrc' => [self::OPT_GRP_MISC, [], self::ARGV_OPTIONAL, 'Manually point to directory with extracted mpq files. This is limited to setup/ (default: setup/mpqdata/)', '=path/' ],
+ 'step' => [self::OPT_GRP_MISC, [], self::ARGV_REQUIRED, 'Start setup at given step (can be used to better automate the setup process).', '=step' ],
);
+ private static $utilScriptRefs = [];
+ private static $setupScriptRefs = [];
+ private static $tmpStore = [];
+ private static $gsFiles = [];
- /**************************/
- /* command line arguments */
- /**************************/
+ public static function registerUtility(UtilityScript $us) : void
+ {
+ if (isset(self::$optDefs[$us::COMMAND]) || isset(self::$utilScriptRefs[$us::COMMAND]))
+ {
+ CLI::write(' Utility function '.CLI::bold($us::COMMAND).' already defined.', CLI::LOG_ERROR);
+ return;
+ }
+ self::$optDefs[$us::COMMAND] = [$us->optGroup, $us->argvOpts, $us->argvFlags, $us::DESCRIPTION, $us::APPENDIX];
+ self::$utilScriptRefs[$us::COMMAND] = $us;
+ }
+
+ public static function registerSetup(string $invoker, SetupScript $ss) : void
+ {
+ if (isset(self::$optDefs[$invoker]) || isset(self::$utilScriptRefs[$invoker]))
+ {
+ CLI::write(' Utility function '.CLI::bold($invoker).' not defined. Can\'t attach Subscript '.CLI::bold($ss->getName()).', invoker is missing. Skipping...', CLI::LOG_ERROR);
+ return;
+ }
+
+ if (isset(self::$setupScriptRefs[$invoker][$ss->getName()]))
+ {
+ CLI::write(' Subscript function '.CLI::bold($ss->getName()).' already defined for invoker '.CLI::bold($invoker).'. Skipping...', CLI::LOG_ERROR);
+ return;
+ }
+
+ if ($childArgs = $ss->getSubCommands())
+ {
+ if ($duplicates = array_intersect(array_keys($childArgs), array_keys(self::$optDefs)))
+ {
+ CLI::write(' Subscript function '.CLI::bold($ss->getName()).'\'s child arguments --'.implode(', --', $duplicates).' are already defined. Skipping...', CLI::LOG_ERROR);
+ return;
+ }
+
+ $newIdx = count(self::$optGroups);
+ self::$optGroups[] = '--' . $invoker . '=' . $ss->getName();
+
+ foreach ($childArgs as $cmd => [$aliases, $argFlags, $description])
+ self::$optDefs[$cmd] = [$newIdx, $aliases, $argFlags, $description, ''];
+ }
+
+ // checks done ... store SetupScript
+ if (self::checkDependencies($ss))
+ {
+ self::$setupScriptRefs[] = [$invoker, $ss->getName(), $ss];
+
+ // recheck temp stored dependencies
+ foreach (self::$tmpStore as $idx => [$invoker, $ts])
+ {
+ if (!self::checkDependencies($ts))
+ continue;
+
+ self::$setupScriptRefs[] = [$invoker, $ts->getName(), $ts];
+ unset(self::$tmpStore[$idx]);
+ }
+ }
+ else // if dependencies haven't been stored yet, put aside for later use
+ self::$tmpStore[] = [$invoker, $ss];
+ }
+
+ private static function checkDependencies(SetupScript &$ss) : bool
+ {
+ if ($ss->isOptional) // optional scripts should no depend on anything
+ return true;
+
+ [$sDep, $bDep] = $ss->getSelfDependencies();
+
+ return ((!$sDep || $sDep == array_intersect($sDep, array_column(array_filter(self::$setupScriptRefs, function($x) { return $x[0] == 'sql'; }), 1))) &&
+ (!$bDep || $bDep == array_intersect($bDep, array_column(array_filter(self::$setupScriptRefs, function($x) { return $x[0] == 'build'; }), 1))));
+ }
+
+ public static function loadScripts() : void
+ {
+ foreach (glob('setup/tools/clisetup/*.us.php') as $file)
+ include_once $file;
+
+ if (self::$tmpStore)
+ {
+ CLI::write('Some SubScripts have unresolved dependencies and have not been loaded', CLI::LOG_ERROR);
+ CLI::write();
+ $tbl = [['Name', '--sql dep.', '--build dep.']];
+ foreach (self::$tmpStore as [$_, $ssRef])
+ {
+ [$sDep, $bDep] = $ssRef->getSelfDependencies();
+
+ $missS = array_intersect($sDep, array_column(array_filter(self::$setupScriptRefs, function($x) { return $x[0] == 'sql'; }), 1));
+ $missB = array_intersect($sDep, array_column(array_filter(self::$setupScriptRefs, function($x) { return $x[0] == 'build'; }), 1));
+
+ array_walk($sDep, function (&$x) use($missS) { $x = in_array($x, $missS) ? $x : CLI::red($x); });
+ array_walk($bDep, function (&$x) use($missB) { $x = in_array($x, $missB) ? $x : CLI::red($x); });
+
+ $tbl[] = [$ssRef->getName(), implode(', ', $sDep), implode(', ', $bDep)];
+ }
+
+ CLI::writeTable($tbl);
+ }
+
+ // link SubScipts back to UtilityScript after all UtilityScripts have been loaded
+ foreach (self::$utilScriptRefs as $name => $us)
+ if (in_array(TrSubScripts::class, class_uses($us)))
+ $us->assignGenerators($name);
+
+ self::evalOpts();
+ }
+
+ public static function getSubScripts(string $invoker = '') : \Generator
+ {
+ foreach (self::$setupScriptRefs as [$src, $name, $ref])
+ if (!$invoker || $src == $invoker)
+ yield $name => [$src, $ref];
+ }
+
+ public static function setLocales() : bool
+ {
+ // optional limit handled locales
+ if (isset(self::$opts['locales']))
+ {
+ $opt = array_map('strtolower', self::$opts['locales']);
+ foreach (Locale::cases() as $loc)
+ if ($loc->validate() && array_intersect(array_map('strtolower', $loc->gameDirs()), $opt))
+ self::$locales[$loc->value] = $loc;
+ }
+ if (!self::$locales)
+ foreach (Locale::cases() as $loc)
+ if ($loc->validate())
+ self::$locales[$loc->value] = $loc;
+
+ return !!self::$locales;
+ }
public static function init() : void
{
- $short = '';
- $long = [];
- $alias = [];
-
- foreach (self::$optDefs as $opt => [$idx, $aliasses, $flags, , ])
- {
- if ($flags & self::ARGV_REQUIRED)
- $opt .= ':';
- else if ($flags & self::ARGV_OPTIONAL)
- $opt .= '::';
-
- $long[] = $opt;
- foreach ($aliasses as $a)
- {
- if ($flags & self::ARGV_REQUIRED) // neither should be set with shortOpts
- $_a = $a.':';
- else if ($flags & self::ARGV_OPTIONAL)
- $_a = $a.'::';
- else
- $_a = $a;
-
- $alias[$a] = $opt;
- if (strlen($a) == 1)
- $short .= $_a;
- else
- $long[] = $_a;
- }
- }
-
- if ($opts = getopt($short, $long))
- foreach ($opts as $o => $v)
- self::$opts[$alias[$o] ?? $o] = (self::$optDefs[$alias[$o] ?? $o][2] & self::ARGV_ARRAY) ? ($v ? explode(',', $v) : []) : ($v ?: true);
+ self::evalOpts();
// optional logging
if (isset(self::$opts['log']))
CLI::initLogFile(trim(self::$opts['log']));
// alternative data source (no quotes, use forward slash)
- if (isset(self::$opts['mpqDataDir']))
- self::$srcDir = CLI::nicePath(self::$opts['mpqDataDir']);
+ if (isset(self::$opts['datasrc']))
+ self::$srcDir = CLI::nicePath('', self::$opts['datasrc']);
- // optional limit handled locales
- if (isset(self::$opts['locales']))
- {
- // engb and enus are identical for all intents and purposes
- $from = ['engb', 'esmx', 'encn'];
- $to = ['enus', 'eses', 'zhcn'];
- $opts['locales'] = str_ireplace($from, $to, strtolower($opts['locales']));
-
- self::$locales = array_intersect(Util::$localeStrings, explode(',', $opts['locales']));
- }
- if (!self::$locales)
- self::$locales = array_filter(Util::$localeStrings);
-
- // restrict actual locales
- foreach (self::$locales as $idx => $str)
- if (!defined('CFG_LOCALES') || CFG_LOCALES & (1 << $idx))
- self::$localeIds[] = $idx;
+ if (!self::setLocales())
+ CLI::write('No valid locale specified. Check your config or --locales parameter, if used', CLI::LOG_ERROR);
// get site status
- if (DB::isConnectable(DB_AOWOW))
- self::$lock = (int)DB::Aowow()->selectCell('SELECT `value` FROM ?_config WHERE `key` = "maintenance"');
+ if (DB::isConnected(DB_AOWOW))
+ self::$lock = Cfg::get('MAINTENANCE');
else
self::$lock = self::LOCK_ON;
}
- public static function getOpt(...$args)
+ public static function writeCLIHelp(bool $full = false) : void
+ {
+ $cmd = self::getOpt(1 << self::OPT_GRP_SETUP | 1 << self::OPT_GRP_UTIL);
+ if (!$cmd || !self::$utilScriptRefs[$cmd[0]]->writeCLIHelp())
+ {
+ $lines = [];
+
+ foreach (self::$optGroups as $idx => $og)
+ {
+ if (!$full && $idx > self::OPT_GRP_SETUP)
+ continue;
+
+ $lines[] = [$og, ''];
+
+ foreach (self::$optDefs as $opt => [$group, $alias, , $desc, $app])
+ {
+ if ($group != $idx)
+ continue;
+
+ $cmd = ' --'.$opt;
+ foreach ($alias as $a)
+ $cmd .= ' | '.(strlen($a) == 1 ? '-'.$a : '--'.$a);
+
+ $lines[] = [$cmd.$app, $desc];
+ }
+ }
+
+ CLI::writeTable($lines);
+ CLI::write();
+ }
+ }
+
+ // called from Setup
+ public static function runInitial() : void
+ {
+ global $argc, $argv; // todo .. find better way? argv, argc are effectivley already global
+
+ // get arguments present in argGroup 1 or 2, if set. Pick first.
+ $cmd = self::getOpt(1 << self::OPT_GRP_SETUP | 1 << self::OPT_GRP_UTIL)[0];
+ $us = &self::$utilScriptRefs[$cmd];
+ $inOut = [null, null, null, null];
+ $allOk = true;
+
+ $i = 0;
+ if ($us::USE_CLI_ARGS)
+ foreach ($argv as $n => $arg)
+ {
+ if (!$n || ($arg && $arg[0] == '-')) // not parent; not handled by getOpt()
+ continue;
+
+ $inOut[$i++] = $arg;
+
+ if ($i > 3)
+ break;
+ }
+
+ if ($dbError = array_filter($us::REQUIRED_DB, function ($x) { return !DB::isConnected($x); }))
+ {
+ CLI::write('Database on index '.implode(', ', $dbError).' not yet set up!', CLI::LOG_ERROR);
+ CLI::write('Please use '.CLI::bold('"php aowow --db"').' for setup', CLI::LOG_BLANK);
+ CLI::write();
+ return;
+ }
+
+ if ($us::LOCK_SITE != self::LOCK_OFF)
+ self::siteLock(self::LOCK_ON);
+
+ if ($us::NOTE_START)
+ CLI::write($us::NOTE_START);
+
+ if (!$us->run($inOut))
+ $allOk = false;
+
+ $error = [];
+ if ($allOk && !$us->test($error))
+ {
+ if ($us::NOTE_ERROR)
+ CLI::write($us::NOTE_ERROR, CLI::LOG_ERROR);
+
+ foreach ($error as $e)
+ CLI::write($e, CLI::LOG_BLANK);
+
+ CLI::write();
+ $allOk = false;
+ }
+
+ if ($allOk)
+ if ($ff = $us->followupFn)
+ if (array_filter($inOut))
+ self::run($ff, $inOut);
+
+ self::siteLock($us::LOCK_SITE == self::LOCK_RESTORE ? self::LOCK_RESTORE : self::LOCK_OFF);
+
+ // end
+ if ($us::NOTE_END_OK && $allOk)
+ CLI::write($us::NOTE_END_OK, CLI::LOG_OK);
+ else if($us::NOTE_END_FAIL && !$allOk)
+ CLI::write($us::NOTE_END_FAIL, CLI::LOG_ERROR);
+ }
+
+ // consecutive calls
+ public static function run(string $cmd, &$args) : bool
+ {
+ if (!isset(self::$utilScriptRefs[$cmd]))
+ return false;
+
+ $us = &self::$utilScriptRefs[$cmd];
+
+ if ($dbError = array_filter($us::REQUIRED_DB, function ($x) { return !DB::isConnected($x); }))
+ {
+ CLI::write('Database on index '.implode(', ', $dbError).' not yet set up!', CLI::LOG_ERROR);
+ CLI::write('Please use '.CLI::bold('"php aowow --db"').' for setup', CLI::LOG_BLANK);
+ CLI::write();
+ return false;
+ }
+
+ if ($us::PROMPT)
+ {
+ CLI::write($us::PROMPT, -1, false);
+ CLI::write();
+
+ if (!CLI::read(['x' => ['Press any key to continue', true, true]], $_)) // we don't actually care about the input
+ return false;
+ }
+
+ $args = array_pad($args, 4, null);
+
+ $success = $us->run($args);
+
+ $error = [];
+ if ($us::NOTE_ERROR && $success && !$us->test($error))
+ {
+ CLI::write($us::NOTE_ERROR, CLI::LOG_ERROR);
+ foreach ($error as $e)
+ CLI::write($e, CLI::LOG_BLANK);
+
+ CLI::write();
+ return false;
+ }
+
+ if ($success)
+ if ($ff = $us->followupFn)
+ if (array_filter($args))
+ if (!self::run($ff, $args))
+ $success = false;
+
+ return $success;
+ }
+
+
+ /**************************/
+ /* command line arguments */
+ /**************************/
+
+ public static function evalOpts() : void
+ {
+ $short = '';
+ $long = [];
+ $alias = [];
+
+ foreach (self::$optDefs as $opt => [, $aliases, $flags, , ])
+ {
+ foreach ($aliases as $i => $a)
+ {
+ if (isset($alias[$a]))
+ $alias[$a][] = $opt;
+ else
+ $alias[$a] = [$opt];
+
+ if ($flags & self::ARGV_REQUIRED)
+ $a .= ':';
+ else if ($flags & self::ARGV_OPTIONAL)
+ $a .= '::';
+
+ if (strlen($aliases[$i]) == 1)
+ $short .= $a;
+ else
+ $long[] = $a;
+ }
+
+ if ($flags & self::ARGV_REQUIRED)
+ $opt .= ':';
+ else if ($flags & self::ARGV_OPTIONAL)
+ $opt .= '::';
+
+ $long[] = $opt;
+ }
+
+ if ($opts = getopt($short, $long))
+ {
+ foreach ($opts as $o => $v)
+ {
+ if (!isset($alias[$o]))
+ self::$opts[$o] = (self::$optDefs[$o][2] & self::ARGV_ARRAY) ? ($v ? explode(',', $v) : []) : ($v ?: true);
+ else
+ foreach ($alias[$o] as $a)
+ self::$opts[$a] = (self::$optDefs[$a][2] & self::ARGV_ARRAY) ? ($v ? explode(',', $v) : []) : ($v ?: true);
+ }
+ }
+ }
+
+ public static function getOpt(/* string|int */ ...$args) // : bool|array|string
{
if (!$args)
return false;
@@ -165,42 +435,15 @@ class CLISetup
return $result;
}
- public static function optHelp(int $groupMask = 0x0) : void
- {
- $lines = [];
-
- foreach (self::$optGroups as $idx => $og)
- {
- if ($groupMask && !($groupMask & (1 << $idx)))
- continue;
-
- $lines[] = [$og, ''];
-
- foreach (self::$optDefs as $opt => [$group, $alias, , $desc, $app])
- {
- if ($group != $idx)
- continue;
-
- $cmd = ' --'.$opt;
- foreach ($alias as $a)
- $cmd .= ' | '.(strlen($a) == 1 ? '-'.$a : '--'.$a);
-
- $lines[] = [$cmd.$app, $desc];
- }
- }
-
- CLI::writeTable($lines);
- }
-
/*******************/
/* web page access */
/*******************/
- public static function siteLock(int $mode = self::LOCK_RESTORE) : void
+ private static function siteLock(int $mode = self::LOCK_RESTORE) : void
{
- if (DB::isConnectable(DB_AOWOW))
- DB::Aowow()->query('UPDATE ?_config SET `value` = ?d WHERE `key` = "maintenance"', (int)!!($mode == self::LOCK_RESTORE ? self::$lock : $mode));
+ if (DB::isConnected(DB_AOWOW))
+ Cfg::set('MAINTENANCE', $mode == self::LOCK_RESTORE ? self::$lock : $mode);
}
@@ -216,40 +459,35 @@ class CLISetup
*/
private static function buildFileList() : bool
{
- CLI::write();
- CLI::write('indexing game data from '.self::$srcDir.' for first time use...');
+ CLI::write('indexing game data from '.self::$srcDir.' for first time use...', CLI::LOG_INFO, true, true);
$setupDirs = glob('setup/*');
foreach ($setupDirs as $sd)
{
- if (mb_substr(self::$srcDir, -1) == '/')
- self::$srcDir = mb_substr(self::$srcDir, 0, -1);
-
- if (mb_substr($sd, -1) == '/')
+ if (mb_substr($sd, -1) == DIRECTORY_SEPARATOR)
$sd = mb_substr($sd, 0, -1);
if (Util::lower($sd) == Util::lower(self::$srcDir))
{
- self::$srcDir = $sd.'/';
+ self::$srcDir = $sd.DIRECTORY_SEPARATOR;
break;
}
}
try
{
- $iterator = new RecursiveDirectoryIterator(self::$srcDir);
- $iterator->setFlags(RecursiveDirectoryIterator::SKIP_DOTS);
+ $iterator = new \RecursiveDirectoryIterator(self::$srcDir);
+ $iterator->setFlags(\RecursiveDirectoryIterator::SKIP_DOTS);
- foreach (new RecursiveIteratorIterator($iterator, RecursiveIteratorIterator::SELF_FIRST) as $path)
+ foreach (new \RecursiveIteratorIterator($iterator, \RecursiveIteratorIterator::SELF_FIRST) as $path)
{
- $_ = str_replace('\\', '/', $path->getPathname());
+ $_ = CLI::nicePath($path->getPathname());
self::$mpqFiles[strtolower($_)] = $_;
}
- CLI::write('done');
- CLI::write();
+ CLI::write('indexing game data from '.self::$srcDir.' for first time use... done!', CLI::LOG_INFO);
}
- catch (UnexpectedValueException $e)
+ catch (\UnexpectedValueException $e)
{
CLI::write('- mpqData dir '.self::$srcDir.' does not exist', CLI::LOG_ERROR);
return false;
@@ -258,7 +496,7 @@ class CLISetup
return true;
}
- public static function fileExists(&$file)
+ public static function fileExists(string &$file) : bool
{
// read mpq source file structure to tree
if (!self::$mpqFiles)
@@ -266,10 +504,10 @@ class CLISetup
return false;
// backslash to forward slash
- $_ = strtolower(str_replace('\\', '/', $file));
+ $_ = strtolower(CLI::nicePath($file));
// remove trailing slash
- if (mb_substr($_, -1, 1) == '/')
+ if (mb_substr($_, -1, 1) == DIRECTORY_SEPARATOR)
$_ = mb_substr($_, 0, -1);
if (isset(self::$mpqFiles[$_]))
@@ -281,7 +519,7 @@ class CLISetup
return false;
}
- public static function filesInPath($path, $useRegEx = false)
+ public static function filesInPath(string $path, bool $useRegEx = false) : array
{
$result = [];
@@ -291,7 +529,7 @@ class CLISetup
return [];
// backslash to forward slash
- $_ = strtolower(str_replace('\\', '/', $path));
+ $_ = strtolower(CLI::nicePath($path));
foreach (self::$mpqFiles as $lowerFile => $realFile)
{
@@ -304,39 +542,119 @@ class CLISetup
return $result;
}
+ public static function filesInPathLocalized(string $pathPattern, ?bool &$status = true, bool $matchAll = true) : array
+ {
+ $result = [];
+
+ foreach (self::$locales as $locId => $loc)
+ {
+ foreach ($loc->gameDirs() as $gDir)
+ {
+ if ($gDir) // if in subDir add trailing slash
+ $gDir .= DIRECTORY_SEPARATOR;
+
+ $path = sprintf($pathPattern, $gDir);
+ if (self::fileExists($path))
+ {
+ $result[$locId] = $path;
+ break;
+ }
+ }
+ }
+
+ if (!$matchAll && !$result)
+ $status = false;
+
+ if ($matchAll && array_diff_key(self::$locales, $result))
+ $status = false;
+
+ return $result;
+ }
+
+ public static function loadGlobalStrings() : bool
+ {
+ CLI::write('loading required GlobalStrings', CLI::LOG_INFO);
+
+ // try to load globalstrings for all selected locales
+ foreach (self::$locales as $locId => $loc)
+ {
+ if (isset(self::$gsFiles[$locId]))
+ continue;
+
+ foreach ($loc->gameDirs() as $gDir)
+ {
+ if ($gDir)
+ $gDir .= DIRECTORY_SEPARATOR;
+
+ $gsFile = sprintf(self::GLOBALSTRINGS_LUA, self::$srcDir, $gDir);
+ if (self::fileExists($gsFile))
+ {
+ self::$gsFiles[$locId] = file($gsFile, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
+ break;
+ }
+ }
+ }
+
+ if ($missing = array_diff_key(self::$locales, self::$gsFiles))
+ {
+ ClI::write('GlobalStrings.lua not found for locale '. Lang::concat($missing, callback: fn($x) => $x->name), CLI::LOG_WARN);
+ return false;
+ }
+
+ return true;
+ }
+
+ public static function searchGlobalStrings(string $pattern) : \Generator
+ {
+ if (!self::$gsFiles)
+ return;
+
+ foreach (self::$gsFiles as $lId => $globalStrings)
+ foreach ($globalStrings as $gs)
+ if (preg_match($pattern, $gs, $result))
+ yield $lId => $result;
+ }
+
/*****************/
/* file handling */
/*****************/
- public static function writeFile($file, $content)
+ public static function writeFile(string $file, string $content) : bool
{
if (Util::writeFile($file, $content))
{
- CLI::write(sprintf(ERR_NONE, CLI::bold($file)), CLI::LOG_OK);
+ CLI::write('created file '. CLI::bold($file), CLI::LOG_OK, true, true);
return true;
}
- $e = error_get_last();
- CLI::write($e['message'].' '.CLI::bold($file), CLI::LOG_ERROR);
return false;
}
- public static function writeDir($dir)
+ public static function writeDir(string $dir, bool &$exist = true) : bool
{
- if (Util::writeDir($dir))
+ if (Util::writeDir($dir, $exist))
+ {
+ if (!$exist)
+ CLI::write('created dir '. CLI::bold($dir), CLI::LOG_OK, true, true);
return true;
+ }
- CLI::write(error_get_last()['message'].' '.CLI::bold($dir), CLI::LOG_ERROR);
return false;
}
- public static function loadDBC($name)
+ public static function loadDBC( string $name) : bool
{
- if (DB::Aowow()->selectCell('SHOW TABLES LIKE ?', 'dbc_'.$name) && DB::Aowow()->selectCell('SELECT count(1) FROM ?#', 'dbc_'.$name))
+ if (!DB::isConnected(DB_AOWOW))
+ {
+ CLI::write('CLISetup::loadDBC() - not connected to DB. Cannot write results!', CLI::LOG_ERROR);
+ return false;
+ }
+
+ if (DB::Aowow()->selectCell('SHOW TABLES LIKE %s', 'dbc_'.$name) && DB::Aowow()->selectCell('SELECT count(1) FROM %n', 'dbc_'.$name))
return true;
- $dbc = new DBC($name, ['temporary' => self::getOpt('delete')]);
+ $dbc = new DBCReader($name, ['temporary' => self::getOpt('delete')]);
if ($dbc->error)
{
CLI::write('CLISetup::loadDBC() - required DBC '.$name.'.dbc not found!', CLI::LOG_ERROR);
diff --git a/setup/tools/clisetup/account.func.php b/setup/tools/clisetup/account.func.php
deleted file mode 100644
index a6637ecc..00000000
--- a/setup/tools/clisetup/account.func.php
+++ /dev/null
@@ -1,65 +0,0 @@
- ['Username', false],
- 'pass1' => ['Enter Password', true ],
- 'pass2' => ['Confirm Password', true ]
- );
-
- User::useLocale(LOCALE_EN);
- Lang::load(Util::$localeStrings[LOCALE_EN]);
-
- if (CLI::read($fields))
- {
- CLI::write();
-
- if (!User::isValidName($fields['name'], $e))
- CLI::write(Lang::account($e == 1 ? 'errNameLength' : 'errNameChars'), CLI::LOG_ERROR);
- else if (!User::isValidPass($fields['pass1'], $e))
- CLI::write(Lang::account($e == 1 ? 'errPassLength' : 'errPassChars'), CLI::LOG_ERROR);
- else if ($fields['pass1'] != $fields['pass2'])
- CLI::write(Lang::account('passMismatch'), CLI::LOG_ERROR);
- else if ($_ = DB::Aowow()->SelectCell('SELECT 1 FROM ?_account WHERE user = ? AND (status <> ?d OR (status = ?d AND statusTimer > UNIX_TIMESTAMP()))', $fields['name'], ACC_STATUS_NEW, ACC_STATUS_NEW))
- CLI::write(Lang::account('nameInUse'), CLI::LOG_ERROR);
- else
- {
- // write to db
- $ok = DB::Aowow()->query('REPLACE INTO ?_account (user, passHash, displayName, joindate, email, allowExpire, userGroups, userPerms) VALUES (?, ?, ?, UNIX_TIMESTAMP(), ?, 0, ?d, 1)',
- $fields['name'],
- User::hashCrypt($fields['pass1']),
- Util::ucFirst($fields['name']),
- CFG_CONTACT_EMAIL,
- U_GROUP_ADMIN
- );
- if ($ok)
- {
- $newId = DB::Aowow()->selectCell('SELECT id FROM ?_account WHERE user = ?', $fields['name']);
- Util::gainSiteReputation($newId, SITEREP_ACTION_REGISTER);
-
- CLI::write("account ".$fields['name']." created successfully", CLI::LOG_OK);
- }
- else // something went wrong
- CLI::write(Lang::main('intError'), CLI::LOG_ERROR);
- }
- }
- else
- {
- CLI::write();
- CLI::write("account creation aborted", CLI::LOG_INFO);
- }
-}
-
-?>
diff --git a/setup/tools/clisetup/account.us.php b/setup/tools/clisetup/account.us.php
new file mode 100644
index 00000000..905ae979
--- /dev/null
+++ b/setup/tools/clisetup/account.us.php
@@ -0,0 +1,134 @@
+ ['Username', false],
+ 'pass1' => ['Enter Password', true ],
+ 'pass2' => ['Confirm Password', true ],
+ 'email' => ['Email (optional)', false]
+ );
+
+ // args: username, password, email, null // iiin
+ public function run(&$args) : bool
+ {
+ Lang::load(Locale::EN);
+
+ $name = $args[0] ?? '';
+ $passw = $args[1] ?? '';
+ $email = $args[2];
+
+ if (Util::validateUsername($name))
+ unset($this->fields['name']);
+ else
+ $name = '';
+
+ if (Util::validatePassword($passw))
+ {
+ unset($this->fields['pass1']);
+ unset($this->fields['pass2']);
+ }
+ else
+ $passw = '';
+
+ if (Util::validateEmail($email))
+ unset($this->fields['email']);
+ else
+ $email = '';
+
+ if ($this->fields && CLI::read($this->fields, $uiAccount) && $uiAccount)
+ {
+ CLI::write();
+
+ if (!$name && !Util::validateUsername($uiAccount['name'], $e) && $e)
+ CLI::write(Lang::account($e == 1 ? 'errNameLength' : 'errNameChars'), CLI::LOG_ERROR);
+ else if (!$name)
+ $name = $uiAccount['name'];
+
+ if (!$passw && !Util::validatePassword($uiAccount['pass1'], $e) && $e)
+ CLI::write($e == 1 ? Lang::account('errPassLength') : Lang::main('intError'), CLI::LOG_ERROR);
+ else if (!$passw && $uiAccount['pass1'] != $uiAccount['pass2'])
+ CLI::write(Lang::account('passMismatch'), CLI::LOG_ERROR);
+ else if (!$passw)
+ $passw = $uiAccount['pass1'];
+
+ if (!$email && !empty($uiAccount['email']) && Util::validateEmail($uiAccount['email']))
+ $email = $uiAccount['email'];
+ else if (!$email && empty($uiAccount['email']))
+ {
+ $email = Cfg::get('CONTACT_EMAIL');
+ CLI::write('[account] no email given, using default: ' . Cfg::get('CONTACT_EMAIL'), CLI::LOG_INFO);
+ }
+ }
+ else if ($this->fields)
+ {
+ CLI::write();
+ CLI::write("[account] admin creation aborted", CLI::LOG_INFO);
+ CLI::write();
+ return true;
+ }
+
+ if (!$name || !$passw || !$email)
+ return false;
+
+ if ($username = DB::Aowow()->selectCell('SELECT `username` FROM ::account WHERE (LOWER(`username`) = LOWER(%s) OR LOWER(`email`) = LOWER(%s)) AND (`status` <> %i OR (`status` = %i AND `statusTimer` > UNIX_TIMESTAMP()))', $name, $email, ACC_STATUS_NEW, ACC_STATUS_NEW))
+ {
+ CLI::write('[account] ' . (Util::lower($name) == Util::lower($username) ? Lang::account('nameInUse') : Lang::account('mailInUse')), CLI::LOG_ERROR);
+ CLI::write();
+ return false;
+ }
+
+ if (DB::Aowow()->qry('REPLACE INTO ::account (`login`, `passHash`, `username`, `joindate`, `email`, `userGroups`, `userPerms`) VALUES (%s, %s, %s, UNIX_TIMESTAMP(), %s, %i, 1)',
+ $name, User::hashCrypt($passw), $name, $email, U_GROUP_ADMIN))
+ {
+ $newId = DB::Aowow()->selectCell('SELECT `id` FROM ::account WHERE LOWER(`username`) = LOWER(%s)', $name);
+ Util::gainSiteReputation($newId, SITEREP_ACTION_REGISTER);
+
+ CLI::write("[account] admin ".$name." created successfully", CLI::LOG_OK);
+ CLI::write();
+
+ return true;
+ }
+
+ CLI::write('[account] ' . Lang::main('intError'), CLI::LOG_ERROR);
+ CLI::write();
+
+ return false;
+ }
+
+ public function test(?array &$error = []) : bool
+ {
+ $error = [];
+ return !!DB::Aowow()->selectCell('SELECT `id` FROM ::account WHERE `userPerms` = 1');
+ }
+});
+
+?>
diff --git a/setup/tools/clisetup/build.func.php b/setup/tools/clisetup/build.func.php
deleted file mode 100644
index 87297309..00000000
--- a/setup/tools/clisetup/build.func.php
+++ /dev/null
@@ -1,94 +0,0 @@
- [$file, $destPath, $deps])
- {
- $reqDBC = [];
-
- if (!in_array($name, FileGen::$subScripts))
- continue;
-
- if (!file_exists(FileGen::$tplPath.$file.'.in'))
- {
- CLI::write(sprintf(ERR_MISSING_FILE, FileGen::$tplPath.$file.'.in'), CLI::LOG_ERROR);
- $allOk = false;
- continue;
- }
-
- if (!CLISetup::writeDir($destPath))
- continue;
-
- $syncIds = []; // todo: fetch what exactly must be regenerated
-
- $ok = FileGen::generate($name, $syncIds);
- if (!$ok)
- $allOk = false;
- else
- $done[] = $name;
-
- CLI::write(' - subscript \''.$file.'\' returned '.($ok ? 'successfully' : 'with errors'), $ok ? CLI::LOG_OK : CLI::LOG_ERROR);
- set_time_limit(FileGen::$defaultExecTime); // reset to default for the next script
- }
-
- // files without template
- foreach (FileGen::$datasets as $file => $deps)
- {
- if (!in_array($file, FileGen::$subScripts))
- continue;
-
- $syncIds = []; // todo: fetch what exactly must be regenerated
-
- $ok = FileGen::generate($file, $syncIds);
- if (!$ok)
- $allOk = false;
- else
- $done[] = $file;
-
- CLI::write(' - subscript \''.$file.'\' returned '.($ok ? 'successfully' : 'with errors'), $ok ? CLI::LOG_OK : CLI::LOG_ERROR);
- set_time_limit(FileGen::$defaultExecTime); // reset to default for the next script
- }
-
- // end
- CLI::write();
- if ($allOk)
- CLI::write('successfully finished file generation', CLI::LOG_OK);
- else
- CLI::write('finished file generation with errors', CLI::LOG_ERROR);
-
- CLISetup::siteLock(CLISetup::LOCK_RESTORE);
- }
- else if (FileGen::getMode() == FileGen::MODE_NORMAL)
- CLI::write('no valid script names supplied', CLI::LOG_ERROR);
-
- return $done;
-}
-
-?>
diff --git a/setup/tools/clisetup/datagen.us.php b/setup/tools/clisetup/datagen.us.php
new file mode 100644
index 00000000..03491690
--- /dev/null
+++ b/setup/tools/clisetup/datagen.us.php
@@ -0,0 +1,169 @@
+';
+ public const NOTE_START = '[sql] begin generation of:';
+ public const NOTE_END_OK = 'successfully finished sql generation';
+ public const NOTE_END_FAIL = 'finished sql generation with errors';
+
+ public const REQUIRED_DB = [DB_AOWOW, DB_WORLD];
+
+ public const LOCK_SITE = CLISetup::LOCK_RESTORE;
+
+ public function __construct()
+ {
+ if ($this->inited)
+ return true;
+
+ $this->defaultExecTime = ini_get('max_execution_time');
+
+ // register subscripts to CLISetup
+ foreach (glob('setup/tools/sqlgen/*.ss.php') as $file)
+ include_once $file;
+
+ $this->inited = true;
+ return true;
+ }
+
+ // args: scriptToDo, scriptSuccess, null, null // ionn
+ public function run(&$args) : bool
+ {
+ $todo = &$args['doSql'];
+ $done = &$args['doneSql'];
+
+ if (!$this->inited)
+ return false;
+
+ // check passed subscript names; limit to real scriptNames
+ if (($sqlArgs = CLISetup::getOpt('sql')) !== false)
+ {
+ if ($sqlArgs === []) // used --sql without arguments
+ $todo = array_keys($this->generators); // do everything
+ else if ($_ = array_intersect(array_keys($this->generators), $sqlArgs))
+ $todo = $_;
+ else
+ {
+ CLI::write('[sql] no valid script names supplied', CLI::LOG_ERROR);
+ return false;
+ }
+
+ // supplement self::NOTE_START
+ CLI::write(' - '.Lang::concat($todo), CLI::LOG_BLANK, false);
+ CLI::write();
+ }
+ else if ($todo)
+ {
+ $todo = array_intersect(array_keys($this->generators), is_array($todo) ? $todo : [$todo]);
+ if (!$todo)
+ return false;
+ }
+ else
+ return false;
+
+ $allOk = true;
+
+ // start file generation
+ foreach ($todo as $cmd)
+ {
+ $syncIds = []; // todo: fetch what exactly must be regenerated
+ $success = false;
+ $scriptRef = &$this->generators[$cmd];
+
+ CLI::write('[sql] filling aowow_'.$cmd.' with data');
+
+ if ($scriptRef->fulfillRequirements())
+ {
+ if ($scriptRef->generate($syncIds))
+ {
+ if (method_exists($scriptRef, 'applyCustomData'))
+ $success = $scriptRef->applyCustomData();
+
+ $success = true;
+ }
+ }
+
+ if (!$success)
+ $allOk = false;
+ else
+ $done[] = $cmd;
+
+ CLI::write('[sql] subscript \''.$cmd.'\' returned '.($success ? 'successfully' : 'with errors'), $success ? CLI::LOG_OK : CLI::LOG_ERROR);
+ CLI::write();
+ set_time_limit($this->defaultExecTime); // reset to default for the next script
+
+ // try to free memory
+ unset($scriptRef, $this->generators[$cmd]);
+ if (gc_enabled())
+ {
+ gc_collect_cycles();
+ gc_mem_caches();
+ }
+ }
+
+ return $allOk;
+ }
+
+ public function writeCLIHelp() : bool
+ {
+ if ($args = CLISetup::getOpt('sql'))
+ {
+ $anyHelp = false;
+ foreach ($args as $cmd)
+ if (isset($this->generators[$cmd]) && $this->generators[$cmd]->writeCLIHelp())
+ $anyHelp = true;
+
+ if ($anyHelp)
+ return true;
+ }
+
+ CLI::write(' usage: php aowow --sql= [--datasrc: --locales:]', -1, false);
+ CLI::write();
+ CLI::write(' Regenerates DB table content for a given SetupScript. Dependencies are taken into account by the triggered calls of --sync and --update', -1, false);
+ CLI::write();
+
+ ksort($this->generators);
+
+ $lines = [['Command', 'TC dependencies', 'AoWoW dependencies', 'Info']];
+ foreach ($this->generators as $cmd => $ssRef)
+ {
+ $tcDeps = explode("\n", Lang::breakTextClean(implode(', ', $ssRef->getRemoteDependencies() ), 35, Lang::FMT_RAW));
+ $aoDeps = explode("\n", Lang::breakTextClean(implode(', ', $ssRef->getSelfDependencies()[0]), 35, Lang::FMT_RAW));
+
+ for ($i = 0; $i < max(count($tcDeps), count($aoDeps)); $i++)
+ $lines[] = array(
+ $i ? '' : $cmd,
+ $tcDeps[$i] ?? '',
+ $aoDeps[$i] ?? '',
+ $i ? '' : $ssRef->getInfo()
+ );
+ }
+
+ CLI::writeTable($lines);
+ CLI::write();
+
+ return true;
+ }
+});
+
+?>
diff --git a/setup/tools/clisetup/dbc.us.php b/setup/tools/clisetup/dbc.us.php
new file mode 100644
index 00000000..de910e6d
--- /dev/null
+++ b/setup/tools/clisetup/dbc.us.php
@@ -0,0 +1,121 @@
+ [tablename [wowbuild]]';
+
+ public const REQUIRED_DB = [DB_AOWOW];
+
+ public const USE_CLI_ARGS = true;
+
+ // args: tblName, wowbuild, null, null // iinn
+ public function run(&$args) : bool
+ {
+ foreach (CLISetup::getOpt('dbc') as $n)
+ {
+ $n = str_ireplace('.dbc', '', trim($n));
+
+ if (empty($n))
+ continue;
+
+ $opts = ['temporary' => false];
+ if ($args[0])
+ $opts['tableName'] = $args[0];
+
+ $dbc = new DBCReader(strtolower($n), $opts, $args[1] ?: DBCReader::DEFAULT_WOW_BUILD);
+ if ($dbc->error)
+ {
+ CLI::write('[dbc] required DBC '.CLI::bold($n).'.dbc not found!', CLI::LOG_ERROR);
+ return false;
+ }
+
+ if (!$dbc->readFile())
+ {
+ CLI::write('[dbc] DBC '.CLI::bold($n).'.dbc could not be written to DB!', CLI::LOG_ERROR);
+ return false;
+ }
+
+ $self = DB::Aowow()->selectCell('SELECT DATABASE()');
+ CLI::write('[dbc] DBC '.CLI::bold($n).'.dbc written to '.CLI::bold('`'.($self ?? 'NULL').'`.`'.$dbc->getTableName().'`').'!', CLI::LOG_OK);
+ }
+
+ return true;
+ }
+
+ public function writeCLIHelp() : bool
+ {
+ CLI::write(' usage: php aowow --dbc= [--locales=] [tablename [wowbuild]]', -1, false);
+ CLI::write();
+ CLI::write(' Extract dbc files from datasrc into sql table. If the dbc file contains a locale block, data from all available locales gets merged into the same table.', -1, false);
+ CLI::write();
+ CLI::write(' Known WoW builds:', -1, false);
+
+ foreach (glob('setup/tools/dbc/*.ini') as $f)
+ {
+ $a = $b = [];
+ preg_match('/(\d+)\.ini$/', $f, $a);
+ if ($h = fopen($f, 'r'))
+ {
+ preg_match('/(\d\.\d.\d\.?\d*)/', fgets($h), $b);
+ fclose($h);
+ }
+
+ CLI::write(' '.CLI::bold($a[1]).' > '.$b[1], -1, false);
+ }
+
+ CLI::write();
+ CLI::write(' Known DBC files:', -1, false);
+
+ $defs = DBCReader::getDefinitions();
+ $letter = '';
+ $buff = [];
+
+ asort($defs);
+
+ foreach ($defs as $d)
+ {
+ if (!$letter)
+ $letter = $d[0];
+ else if ($letter != $d[0])
+ {
+ CLI::write(' '.CLI::bold(Util::ucFirst($letter)).':', -1, false);
+ foreach (explode("\n", Lang::breakTextClean(implode(', ', $buff), 120, Lang::FMT_RAW)) as $line)
+ CLI::write(' '.$line, -1, false);
+
+ $buff = [$d];
+ $letter = $d[0];
+ }
+ else
+ $buff[] = $d;
+ }
+
+ CLI::write(' '.CLI::bold(Util::ucFirst($letter)).':', -1, false);
+ foreach (explode("\n", Lang::breakTextClean(implode(', ', $buff), 120, Lang::FMT_RAW)) as $line)
+ CLI::write(' '.$line, -1, false);
+
+ CLI::write();
+ CLI::write();
+
+ return true;
+ }
+});
+
+?>
diff --git a/setup/tools/clisetup/dbconfig.func.php b/setup/tools/clisetup/dbconfig.func.php
deleted file mode 100644
index 4bf24d88..00000000
--- a/setup/tools/clisetup/dbconfig.func.php
+++ /dev/null
@@ -1,147 +0,0 @@
- ['Server Host', false],
- 'user' => ['User', false],
- 'pass' => ['Password', true ],
- 'db' => ['Database Name', false],
- 'prefix' => ['Table prefix', false]
- );
- $testDB = function($idx, $name, $dbInfo)
- {
- $buff = '['.CLI::bold($idx).'] '.str_pad($name, 17);
-
- if ($dbInfo['host'])
- {
- DB::test($dbInfo, $errStr);
-
- $buff .= $errStr ? CLI::red('ERR ') : CLI::green('OK ');
- $buff .= 'mysqli://'.$dbInfo['user'].':'.str_pad('', mb_strlen($dbInfo['pass']), '*').'@'.$dbInfo['host'].'/'.$dbInfo['db'];
- $buff .= ($dbInfo['prefix'] ? ' table prefix: '.$dbInfo['prefix'] : null).' '.$errStr;
- }
- else
- $buff .= ' '.CLI::bold('');
-
- return $buff;
- };
-
- if (file_exists('config/config.php'))
- require 'config/config.php';
-
- foreach ($databases as $idx => $name)
- if (empty($AoWoWconf[$name]) && $name != 'characters' )
- $AoWoWconf[$name] = array_combine(array_keys($dbFields), ['', '', '', '', '']);
-
- while (true)
- {
- CLI::write();
- CLI::write("select a numerical index to use the corresponding entry");
-
- $nCharDBs = 0;
- foreach ($databases as $idx => $name)
- {
- if ($idx != 3)
- CLI::write($testDB($idx, $name, $AoWoWconf[$name]));
- else if (!empty($AoWoWconf[$name]))
- foreach ($AoWoWconf[$name] as $charIdx => $dbInfo)
- CLI::write($testDB($idx + $nCharDBs++, $name.' ['.$charIdx.']', $AoWoWconf[$name][$charIdx]));
- }
-
- CLI::write("[".CLI::bold(3 + $nCharDBs)."] add an additional Character DB");
-
- while (true)
- {
- $inp = ['idx' => ['', true, '/\d/']];
- if (CLI::read($inp, true) && $inp)
- {
- if ($inp['idx'] >= 0 && $inp['idx'] <= (3 + $nCharDBs))
- {
- $curFields = $inp['idx'] ? $dbFields : array_slice($dbFields, 0, 4);
-
- if ($inp['idx'] == 3 + $nCharDBs) // add new realmDB
- $curFields['realmId'] = ['Realm Id', false, '/[1-9][0-9]*/'];
-
- if (CLI::read($curFields))
- {
- if ($inp['idx'] == 0 && $curFields)
- $curFields['prefix'] = 'aowow_';
-
- // auth, world or aowow
- if ($inp['idx'] < 3)
- $AoWoWconf[$databases[$inp['idx']]] = $curFields ?: array_combine(array_keys($dbFields), ['', '', '', '', '']);
- // new char DB
- else if ($inp['idx'] == 3 + $nCharDBs)
- {
- if ($curFields)
- {
- $_ = $curFields['realmId'];
- unset($curFields['realmId']);
- $AoWoWconf[$databases[3]][$_] = $curFields;
- }
- }
- // existing char DB
- else
- {
- $i = 0;
- foreach ($AoWoWconf[$databases[3]] as $realmId => &$dbInfo)
- {
- if ($inp['idx'] - 3 != $i++)
- continue;
-
- if ($curFields)
- $dbInfo = $curFields;
- else
- unset($AoWoWconf[$databases[3]][$realmId]);
- }
- }
-
- // write config file
- $buff = " $charInfo)
- $buff .= '$AoWoWconf[\''.$db.'\'][\''.$idx.'\'] = '.var_export($AoWoWconf[$db][$idx], true).";\n\n";
- }
- $buff .= "?>\n";
- CLI::write();
- CLISetup::writeFile('config/config.php', $buff);
- continue 2;
- }
- else
- {
- CLI::write();
- CLI::write("edit canceled! returning to list...", CLI::LOG_INFO);
- sleep(1);
- continue 2;
- }
- }
- }
- else
- {
- CLI::write();
- CLI::write("leaving db setup...", CLI::LOG_INFO);
- break 2;
- }
- }
- }
-}
-
-?>
diff --git a/setup/tools/clisetup/dbconfig.us.php b/setup/tools/clisetup/dbconfig.us.php
new file mode 100644
index 00000000..e2120989
--- /dev/null
+++ b/setup/tools/clisetup/dbconfig.us.php
@@ -0,0 +1,335 @@
+ ['Server Host', false],
+ 'user' => ['User', false],
+ 'pass' => ['Password', true ],
+ 'db' => ['Database Name', false],
+ 'prefix' => ['Table prefix', false]
+ );
+
+ private $icons = ['Normal[0]', 'PvP', null, null, 'Normal[4]', 'RP', null, 'RP-PvP'];
+ private $regions = array(
+ null, 'Development', 'United States', 'Oceanic', 'Latin America', 'Tournament (Americas)', 'Korea', 'Tournament (Korea)', 'English', 'German', 'French', 'Spanish', 'Russian', 'Tournament (EU)', 'Taiwan', 'Tournament (Taiwan)', 'China',
+ 25 => 'Tournament (China)', 26 => 'Test Server', 27 => 'Tournament (Test Server)', 28 => 'QA Server', 30 => 'Test Server 2'
+ );
+
+ // args: null, null, null, null // nnnn
+ public function run(&$args) : bool
+ {
+ if (!$this->config && file_exists(self::CONFIG_FILE))
+ {
+ require self::CONFIG_FILE;
+ $this->config = $AoWoWconf;
+ unset($AoWoWconf);
+ }
+
+ foreach ($this->databases as $idx => $name)
+ if (empty($this->config[$name]) && $name != 'characters' )
+ $this->config[$name] = array_combine(array_keys($this->dbFields), ['', '', '', '', '']);
+
+ while (true)
+ {
+ CLI::write("select an index to use the corresponding entry", -1, false);
+
+ $nCharDBs = 0;
+ $tblRows = [];
+ foreach ($this->databases as $idx => $name)
+ {
+ if ($idx != DB_CHARACTERS)
+ $tblRows[] = $this->testDB($idx, $name, $this->config[$name]);
+ else if (!empty($this->config[$name]))
+ foreach ($this->config[$name] as $charIdx => $dbInfo)
+ $tblRows[] = $this->testDB($idx + $nCharDBs++, $name.' ['.$charIdx.']', $this->config[$name][$charIdx]);
+ }
+
+ $tblRows[] = ['['.CLI::bold('N').']', 'new characters DB'];
+ $tblRows[] = ['['.CLI::bold('S').']', 'show available realms'];
+ $tblRows[] = ['['.CLI::bold('R').']', 'retest / reload DBs'];
+ CLI::writeTable($tblRows, false, true);
+
+ while (true)
+ {
+ if (CLI::read(['idx' => ['', true, true, '/\d|R|N|S/i']], $uiIndex) && $uiIndex)
+ {
+ if (strtoupper($uiIndex['idx']) == 'R')
+ continue 2;
+ else if (strtoupper($uiIndex['idx']) == 'S')
+ {
+ CLI::write();
+ if (!DB::isConnectable(DB_AUTH) || !$this->test())
+ CLI::write('[db] auth db not yet set up.', CLI::LOG_ERROR);
+ else if ($realms = DB::Auth()->selectAssoc('SELECT `id` AS "0", `name` AS "1", `icon` AS "2", `timezone` AS "3", `allowedSecurityLevel` AS "4" FROM realmlist'))
+ {
+ $tbl = [['Realm Id', 'Name', 'Type', 'Region', 'GMLevel', 'Status']];
+ foreach ($realms as [$id, $name, $icon, $region, $level])
+ {
+ $status = [];
+ $hasRegion = false;
+ foreach (Profiler::REGIONS as $n => $valid)
+ if ($hasRegion = in_array($region, $valid))
+ {
+ if ($n == 'dev')
+ $status[] = 'Restricted region (staff only)';
+ break;
+ }
+
+ if (!$hasRegion && !$status)
+ $status[] = 'Unsupported region';
+ if ($level > 0)
+ $status[] = 'GM-Level locked';
+ if (DB::isConnectable(DB_CHARACTERS . $id))
+ $status[] = 'Already in use';
+
+ $tbl[] = [$id, $name, $this->icons[$icon] ?? '', $this->regions[$region] ?? '', $level, $status ? CLI::yellow(implode(', ', $status)) : CLI::green('Usable')];
+ }
+
+ CLI::writeTable($tbl);
+ }
+ else
+ CLI::write('[db] table `realmlist` is empty.', CLI::LOG_WARN);
+
+ CLI::write();
+
+ continue 2;
+ }
+ else if (($uiIndex['idx'] >= DB_AOWOW && $uiIndex['idx'] < (DB_CHARACTERS + $nCharDBs)) || strtoupper($uiIndex['idx']) == 'N')
+ {
+ $curFields = $uiIndex['idx'] ? $this->dbFields : array_slice($this->dbFields, 0, 4);
+
+ if (strtoupper($uiIndex['idx']) == 'N') // add new characters DB
+ $curFields['realmId'] = ['Realm Id', false, false, '/\d{1,3}/'];
+
+ if (CLI::read($curFields, $uiRealm))
+ {
+ if ($uiIndex['idx'] == DB_AOWOW && $uiRealm)
+ $uiRealm['prefix'] = 'aowow_';
+
+ if (strtoupper($uiIndex['idx']) == 'N') // new char DB
+ {
+ if ($uiRealm)
+ {
+ $_ = $uiRealm['realmId'];
+ unset($uiRealm['realmId']);
+ $this->config[$this->databases[DB_CHARACTERS]][$_] = $uiRealm;
+ }
+ }
+ else if ($uiIndex['idx'] < DB_CHARACTERS) // auth, world or aowow
+ $this->config[$this->databases[$uiIndex['idx']]] = $uiRealm ?: array_combine(array_keys($this->dbFields), ['', '', '', '', '']);
+ else // existing char DB
+ {
+ $i = 0;
+ foreach ($this->config[$this->databases[DB_CHARACTERS]] as $realmId => &$dbInfo)
+ {
+ if ($uiIndex['idx'] - DB_CHARACTERS != $i++)
+ continue;
+
+ if ($uiRealm)
+ $dbInfo = $uiRealm;
+ else
+ unset($this->config[$this->databases[3]][$realmId]);
+ }
+ }
+
+ // write config file
+ $buff = "databases as $db)
+ {
+ if ($db != 'characters')
+ $buff .= '$AoWoWconf[\''.$db.'\'] = '.var_export($this->config[$db], true).";\n\n";
+ else if (isset($this->config[$db]))
+ foreach ($this->config[$db] as $idx => $charInfo)
+ $buff .= '$AoWoWconf[\''.$db.'\'][\''.$idx.'\'] = '.var_export($this->config[$db][$idx], true).";\n\n";
+ }
+ $buff .= "?>\n";
+ CLI::write();
+ CLISetup::writeFile(self::CONFIG_FILE, $buff);
+ continue 2;
+ }
+ else
+ {
+ CLI::write("[db] edit canceled! returning to list...", CLI::LOG_INFO);
+ CLI::write();
+ sleep(1);
+ continue 2;
+ }
+ }
+ }
+ else
+ {
+ CLI::write("[db] leaving db config...", CLI::LOG_INFO);
+ CLI::write();
+ break 2;
+ }
+ }
+ }
+
+ return true;
+ }
+
+ public function test(?array &$error = []) : bool
+ {
+ if (!$this->config)
+ {
+ require self::CONFIG_FILE;
+ $this->config = $AoWoWconf;
+ unset($AoWoWconf);
+ }
+
+ $error = [];
+ foreach (['aowow', 'world', 'auth'] as $idx => $what)
+ {
+ if ($what == 'auth' && (empty($this->config['auth']) || empty($this->config['auth']['host'])))
+ continue;
+
+ // init proper access for further setup
+ if (DB::test($this->config[$what], $err))
+ {
+ DB::load($idx, $this->config[$what]);
+ switch ($idx)
+ {
+ case DB_AOWOW:
+ if (DB::Aowow()->selectCell('SHOW TABLES LIKE %s', 'aowow_dbversion'))
+ Cfg::load(); // first time load after successful db setup
+ else
+ $error[] = ' * '.$what.': doesn\'t seem to contain aowow tables!';
+ break;
+ case DB_WORLD:
+ if (!DB::World()->selectCell('SHOW TABLES LIKE %s', 'version'))
+ $error[] = ' * '.$what.': doesn\'t seem to contain TrinityCore world tables!';
+ else if (DB::World()->selectCell('SELECT `cache_id` FROM `version`') < TDB_WORLD_MINIMUM_VER)
+ $error[] = ' * '.$what.': TDB world db is structurally outdated! (min rev.: '.CLI::bold(TDB_WORLD_MINIMUM_VER).')';
+ break;
+ default:
+ // no further checks at this time
+ }
+ }
+ else
+ $error[] = ' * '.$what.': '.$err;
+ }
+
+ return empty($error);
+ }
+
+ private function testDB($idx, $name, $dbInfo)
+ {
+ $buff = ['['.CLI::bold($idx).']', $name];
+
+ if ($dbInfo['host'])
+ {
+ $result = CLI::green('OK');
+ $note = '';
+
+ if (DB::test($dbInfo, $note))
+ {
+ DB::load($idx, $dbInfo);
+
+ $ok = false;
+ switch ($idx)
+ {
+ case DB_AOWOW:
+ if (DB::Aowow()->selectCell('SHOW TABLES LIKE %s', 'aowow_dbversion'))
+ {
+ if ($date = DB::Aowow()->selectCell('SELECT `date` FROM ::dbversion'))
+ {
+ $note = 'AoWoW DB version @ ' . date(Util::$dateFormatInternal, $date);
+ $ok = true;
+ }
+ else
+ $note = CLI::yellow('AoWoW DB version empty! Import of DB dump failed?');
+ }
+ else
+ $note = CLI::yellow('DB test failed to find dbversion table. ').CLI::bold('setup/sql/01-db_structure.sql').CLI::yellow(' not yet imported?');
+ break;
+ case DB_WORLD:
+ if (DB::World()->selectCell('SHOW TABLES LIKE %s', 'version'))
+ {
+ [$vString, $vNo] = DB::World()->selectRow('SELECT `db_version` AS "0", `cache_id` AS "1" FROM `version`');
+ if (strpos($vString, 'TDB') === 0)
+ {
+ if ($vNo < TDB_WORLD_MINIMUM_VER)
+ $note = CLI::yellow('DB test found TrinityDB version older than rev. ').CLI::bold(TDB_WORLD_MINIMUM_VER).CLI::yellow('. Please update to at least rev. ').CLI::bold(TDB_WORLD_MINIMUM_VER);
+ else if ($vNo > TDB_WORLD_EXPECTED_VER)
+ $note = CLI::yellow('DB test found TrinityDB version newer than rev. ').CLI::bold(TDB_WORLD_EXPECTED_VER).CLI::yellow('. Be advised! DB structure may diverge!');
+ else
+ {
+ $note = 'TrinityDB version @ ' . $vString;
+ $ok = true;
+ }
+ }
+ else if (strpos($vString, 'ACDB') === 0)
+ $note = CLI::yellow('DB test found AzerothCore DB version. AzerothCore DB structure is not supported!');
+ else
+ $note = CLI::yellow('DB test found unexpected vendor in expected version table. Uhh.. Good Luck..!?');
+ }
+ else if (DB::World()->selectCell('SHOW TABLES LIKE %s', 'db_version'))
+ $note = CLI::yellow('DB test found MaNGOS styled version table. MaNGOS DB structure is not supported!');
+ else
+ $note = CLI::yellow('DB test failed to find version table. TrinityDB world not yet imported?');
+ break;
+ default:
+ $ok = true; // no tests right now
+ }
+
+ if (!$ok)
+ $result = CLI::yellow('WARN');
+ }
+ else
+ $result = CLI::red('ERR');
+
+ $buff[] = $result;
+ $buff[] = 'mysqli://'.$dbInfo['user'].':'.($dbInfo['pass'] ? '**********' : '').'@'.$dbInfo['host'].'/'.$dbInfo['db'];
+ $buff[] = $dbInfo['prefix'] ? 'pre.: '.$dbInfo['prefix'] : '';
+ $buff[] = $note;
+ }
+ else if ($idx == DB_AUTH)
+ $buff[] = CLI::bold('');
+ else
+ $buff[] = CLI::bold('');
+
+ return $buff;
+ }
+
+ public function writeCLIHelp() : bool
+ {
+ CLI::write(' Connecting to '.CLI::bold('`auth`').' is optional and only required when using the character profiler tool or using the game accounts for login.', -1, false);
+ CLI::write();
+ CLI::write(' Connecting to '.CLI::bold('`characters`').' is optional and only required when using the character profiler tool.', -1, false);
+ CLI::write(' Remember to use the correct Realm Id from '.CLI::bold('`auth`.`realmlist`').' when connecting to your characters DB.', -1, false);
+ CLI::write();
+ CLI::write(' To remove a db entry edit it and leave all fields empty.', -1, false);
+ CLI::write();
+ CLI::write();
+
+ return true;
+ }
+});
+
+?>
diff --git a/setup/tools/clisetup/filegen.us.php b/setup/tools/clisetup/filegen.us.php
new file mode 100644
index 00000000..426a0769
--- /dev/null
+++ b/setup/tools/clisetup/filegen.us.php
@@ -0,0 +1,184 @@
+';
+ public const NOTE_START = '[build] begin generation of:';
+ public const NOTE_END_OK = 'successfully finished file generation';
+ public const NOTE_END_FAIL = 'finished file generation with errors';
+
+ public const REQUIRED_DB = [DB_AOWOW, DB_WORLD];
+
+ public const LOCK_SITE = CLISetup::LOCK_RESTORE;
+
+ private $uploadDirs = array( // stuff that should be writable by www-data and isn't directly created by setup steps
+ 'static/uploads/screenshots/normal/',
+ 'static/uploads/screenshots/pending/',
+ 'static/uploads/screenshots/resized/',
+ 'static/uploads/screenshots/temp/',
+ 'static/uploads/screenshots/thumb/',
+ 'static/uploads/temp/',
+ 'static/uploads/guide/images/',
+ 'static/uploads/avatars/'
+ );
+
+ public function __construct()
+ {
+ if ($this->inited)
+ return true;
+
+ $this->defaultExecTime = ini_get('max_execution_time');
+
+ // register subscripts to CLISetup
+ foreach (glob('setup/tools/filegen/*.ss.php') as $file)
+ include_once $file;
+
+ $this->inited = true;
+ return true;
+ }
+
+ // args: scriptToDo, scriptSuccess, null, null // ionn
+ public function run(&$args) : bool
+ {
+ $todo = &$args['doBuild'];
+ $done = &$args['doneBuild'];
+
+ if (!$this->inited)
+ return false;
+
+
+ // check passed subscript names; limit to real scriptNames
+ if (($buildArgs = CLISetup::getOpt('build')) !== false)
+ {
+ if ($buildArgs === []) // used --build without arguments
+ $todo = array_keys($this->generators); // do everything
+ else if ($_ = array_intersect(array_keys($this->generators), $buildArgs))
+ $todo = $_;
+ else
+ {
+ CLI::write('[build] no valid script names supplied', CLI::LOG_ERROR);
+ return false;
+ }
+
+ // supplement self::NOTE_START
+ CLI::write(' - '.Lang::concat($todo), CLI::LOG_BLANK, false);
+ CLI::write();
+ }
+ else if ($todo)
+ {
+ $todo = array_intersect(array_keys($this->generators), is_array($todo) ? $todo : [$todo]);
+ if (!$todo)
+ return false;
+ }
+ else
+ return false;
+
+ // create user upload dir structure
+ foreach ($this->uploadDirs as $ud)
+ {
+ if (CLISetup::writeDir($ud))
+ continue;
+
+ CLI::write('[build] could not create directory: '.CLI::bold($ud), CLI::LOG_ERROR);
+ return false;
+ }
+
+ $done = [];
+ $allOk = true;
+
+ // start file generation
+ foreach ($todo as $cmd)
+ {
+ $success = false;
+ $scriptRef = &$this->generators[$cmd];
+
+ CLI::write('[build] gathering data for '.$cmd);
+
+ if ($scriptRef->fulfillRequirements())
+ $success = $scriptRef->generate();
+
+ if (!$success)
+ $allOk = false;
+ else
+ $done[] = $cmd;
+
+ CLI::write('[build] subscript \''.$cmd.'\' returned '.($success ? 'successfully' : 'with errors'), $success ? CLI::LOG_OK : CLI::LOG_ERROR);
+ CLI::write();
+
+ set_time_limit($this->defaultExecTime); // reset to default for the next script
+
+ // try to free memory
+ unset($scriptRef, $this->generators[$cmd]);
+ if (gc_enabled())
+ {
+ gc_collect_cycles();
+ gc_mem_caches();
+ }
+ }
+
+ return $allOk;
+ }
+
+ public function writeCLIHelp() : bool
+ {
+ if ($args = CLISetup::getOpt('build'))
+ {
+ $anyHelp = false;
+ foreach ($args as $cmd)
+ if (isset($this->generators[$cmd]) && $this->generators[$cmd]->writeCLIHelp())
+ $anyHelp = true;
+
+ if ($anyHelp)
+ return true;
+ }
+
+ CLI::write(' usage: php aowow --build= [--datasrc: --locales:]', -1, false);
+ CLI::write();
+ CLI::write(' Compiles files for a given SetupScript. Existing files are kept by default. Dependencies are taken into account by the triggered calls of --sync --update', -1, false);
+ CLI::write();
+
+ ksort($this->generators);
+
+ $lines = [['Command', 'TC dependencies', 'AoWoW dependencies', 'Info']];
+ foreach ($this->generators as $cmd => $ssRef)
+ {
+ $tcDeps = explode("\n", Lang::breakTextClean(implode(', ', $ssRef->getRemoteDependencies() ), 35, Lang::FMT_RAW));
+ $aoDeps = explode("\n", Lang::breakTextClean(implode(', ', $ssRef->getSelfDependencies()[0]), 35, Lang::FMT_RAW));
+
+ for ($i = 0; $i < max(count($tcDeps), count($aoDeps)); $i++)
+ $lines[] = array(
+ $i ? '' : $cmd,
+ $tcDeps[$i] ?? '',
+ $aoDeps[$i] ?? '',
+ $i ? '' : $ssRef->getInfo()
+ );
+ }
+
+ CLI::writeTable($lines);
+ CLI::write();
+
+ return true;
+ }
+});
+
+?>
diff --git a/setup/tools/clisetup/setup.func.php b/setup/tools/clisetup/setup.func.php
deleted file mode 100644
index dfd4a401..00000000
--- a/setup/tools/clisetup/setup.func.php
+++ /dev/null
@@ -1,347 +0,0 @@
- $what)
- {
- if ($what == 'auth' && (empty($AoWoWconf['auth']) || empty($AoWoWconf['auth']['host'])))
- continue;
-
- // init proper access for further setup
- if (DB::test($AoWoWconf[$what], $err))
- DB::load($idx, $AoWoWconf[$what]);
- else
- $error[] = ' * '.$what.': '.$err;
- }
-
- return empty($error);
- }
-
- function testSelf(array &$error) : bool
- {
- $error = [];
- $test = function(&$protocol, &$host, $testFile, &$rCode)
- {
- $res = get_headers($protocol.$host.$testFile, true);
-
- if (!preg_match("/HTTP\/[0-9\.]+\s+([0-9]+)/", $res[0], $m))
- return false;
-
- $rCode = $m[1];
-
- if ($rCode == 200)
- return true;
-
- if ($rCode == 301 || $rCode == 302)
- {
- if (!empty($res['Location']) && preg_match("/(https?:\/\/)(.*)".strtr($testFile, ['/' => '\/', '.' => '\.'])."/i", $res['Location'], $n))
- {
- $protocol = $n[1];
- $host = $n[2];
- }
-
- return false;
- }
-
- $rCode = 0;
- return false;
- };
-
- $res = DB::Aowow()->selectCol('SELECT `key` AS ARRAY_KEY, value FROM ?_config WHERE `key` IN ("site_host", "static_host", "force_ssl")');
- $prot = $res['force_ssl'] ? 'https://' : 'http://';
- $cases = array(
- 'site_host' => [$prot, $res['site_host'], '/README.md'],
- 'static_host' => [$prot, $res['static_host'], '/css/aowow.css']
- );
-
- foreach ($cases as $conf => [$protocol, $host, $testFile])
- {
- if ($host)
- {
- if (!$test($protocol, $host, $testFile, $resp))
- {
- if ($resp == 301 || $resp == 302)
- {
- CLI::write('self test received status '.CLI::bold($resp).' (page moved) for '.$conf.', pointing to: '.$protocol.$host.$testFile, CLI::LOG_WARN);
- $inp = ['x' => ['should '.CLI::bold($conf).' be set to '.CLI::bold($host).' and force_ssl be updated? (y/n)', true, '/y|n/i']];
- if (!CLI::read($inp, true) || !$inp || strtolower($inp['x']) == 'n')
- $error[] = ' * '.$protocol.$host.$testFile.' ['.$resp.']';
- else
- {
- DB::Aowow()->query('UPDATE ?_config SET `value` = ? WHERE `key` = ?', $host, $conf);
- DB::Aowow()->query('UPDATE ?_config SET `value` = ?d WHERE `key` = "force_ssl"', intVal($protocol == 'https://'));
- }
-
- CLI::write();
- }
- else
- $error[] = ' * '.$protocol.$host.$testFile.' ['.$resp.']';
- }
- }
- else
- $error[] = ' * '.strtoupper($conf).' is empty';
- }
-
- return empty($error);
- }
-
- function testAcc(array &$error) : bool
- {
- $error = [];
- return !!DB::Aowow()->selectCell('SELECT `id` FROM ?_account WHERE `userPerms` = 1');
- }
-
-
- /********************/
- /* get current step */
- /********************/
-
- $startStep = 0;
- if (file_exists('cache/firstrun'))
- {
- $rows = file('cache/firstrun');
- if ((int)$rows[0] == AOWOW_REVISION)
- $startStep = (int)$rows[1];
- }
-
- if (CLISetup::getOpt('help'))
- {
- CLI::write();
- CLI::write(' usage: php aowow --setup [--locales: --mpqDataDir:]', -1, false);
- CLI::write();
- CLI::write(' Initially essential connection information are set up and basic connectivity tests are run afterwards.', -1, false);
- CLI::write(' In the main stage dbc and world data is compiled into the database and required sound, image and data files are generated.', -1, false);
- CLI::write(' This does not require further input and will take about 15-20 minutes, plus 10 minutes per additional locale.', -1, false);
- CLI::write(' Lastly pending updates are applied and you are prompted to create an administrator account.', -1, false);
-
- if ($startStep)
- {
- CLI::write();
- CLI::write(' You are currently on step '.($startStep + 1).' / '.count($steps).'. You can resume or restart the setup process.', -1, false);
- }
-
- CLI::write();
- return;
- }
-
- if ($startStep)
- {
-
- CLI::write('Found firstrun progression info. (Halted on subscript '.($steps[$startStep][1][0] ? $steps[$startStep][1] : $steps[$startStep][0]).')', CLI::LOG_INFO);
- $inp = ['x' => ['continue setup? (y/n)', true, '/y|n/i']];
- $msg = '';
- if (!CLI::read($inp, true) || !$inp || strtolower($inp['x']) == 'n')
- {
- $msg = 'Starting setup from scratch...';
- $startStep = 0;
- }
- else
- $msg = 'Resuming setup from step '.$startStep.'...';
-
- CLI::write();
- CLI::write($msg);
- sleep(1);
- }
-
-
- /*******/
- /* run */
- /*******/
-
- CLISetup::siteLock(CLISetup::LOCK_ON);
-
- foreach ($steps as $idx => $step)
- {
- if ($startStep > $idx)
- continue;
-
- if (!strpos($step[0], '::') && !is_callable($step[0]))
- require_once 'setup/tools/clisetup/'.$step[0].'.func.php';
-
- if ($step[3])
- {
- CLI::write($step[3]);
-
- $inp = ['x' => ['Press any key to continue', true]];
- if (!CLI::read($inp, true)) // we don't actually care about the input
- return;
- }
-
- while (true)
- {
- if (strpos($step[0], '::'))
- $res = call_user_func($step[0], $step[1]);
- else
- $res = $step[0](...$step[1]);
-
- // check script result
- if ($step[2])
- {
- $errors = [];
- if (!$step[2]($errors))
- {
- CLI::write($step[4], CLI::LOG_ERROR);
- foreach ($errors as $e)
- CLI::write($e);
- }
- else
- {
- $saveProgress($idx);
- break;
- }
- }
- else if ($res !== false)
- {
- $saveProgress($idx);
- break;
- }
-
- $inp = ['x' => ['['.CLI::bold('c').']ontinue anyway? ['.CLI::bold('r').']etry? ['.CLI::bold('a').']bort?', true, '/c|r|a/i']];
- if (CLI::read($inp, true) && $inp)
- {
- CLI::write();
- switch(strtolower($inp['x']))
- {
- case 'c':
- $saveProgress($idx);
- break 2;
- case 'r':
- break;
- case 'a':
- return;
- }
- }
- else
- {
- CLI::write();
- return;
- }
- }
- }
-
- unlink('cache/firstrun');
- CLISetup::siteLock(CLISetup::LOCK_OFF);
- CLI::write('setup finished', CLI::LOG_OK);
- return;
-}
-
-?>
diff --git a/setup/tools/clisetup/setup.us.php b/setup/tools/clisetup/setup.us.php
new file mode 100644
index 00000000..5b8c4762
--- /dev/null
+++ b/setup/tools/clisetup/setup.us.php
@@ -0,0 +1,208 @@
+ [], 'doBuild' => []]; // ref to pass commands from 'update' to 'sync'
+ private $steps = array(
+ // [staticUS, $name, [...args]]
+ ['database', '', []],
+ ['configure', '', []],
+ // sql- and build- stuff here
+ ['update', '', []],
+ ['sync', '', []],
+ ['account', '', []]
+ );
+
+ private const STEP_FILE = 'cache/setup/firstrun';
+
+ private function getSavedStartStep() : int
+ {
+ if (file_exists(self::STEP_FILE))
+ {
+ $rows = file(self::STEP_FILE);
+ if ((int)$rows[0] == AOWOW_REVISION)
+ return (int)$rows[1];
+ }
+
+ return 0;
+ }
+
+ // Note! Must be loaded after all SetupScripts have been registered
+ public function __construct()
+ {
+ /****************/
+ /* define steps */
+ /****************/
+
+ # link required steps with param
+ $this->steps[2][2] = &$this->dynArgs; // update
+ $this->steps[3][2] = &$this->dynArgs; // sync
+
+ # from /sqlgen + /filegen .. already sorted by CLISetup
+ foreach (CLISetup::getSubScripts() as $name => [$invoker, $ssRef])
+ {
+ if ($ssRef->isOptional)
+ continue;
+
+ if ($invoker == 'sql')
+ array_splice($this->steps, -3, 0, [[$invoker, $name, ['doSql' => $name]]]);
+ else if ($invoker == 'build')
+ array_splice($this->steps, -3, 0, [[$invoker, $name, ['doBuild' => $name]]]);
+ }
+ }
+
+ // args: null, null, null, null // nnnn
+ public function run(&$args) : bool
+ {
+ /******************/
+ /* get start step */
+ /******************/
+
+ $startStep = 0;
+ if (($cliStartStep = CLISetup::getOpt('step')) !== false)
+ {
+ $startStep = ((int)$cliStartStep) - 1;
+ if ($startStep < 0 || $startStep >= count($this->steps))
+ {
+ CLI::write('Invalid step number. Use --step <1-'.count($this->steps).'>', CLI::LOG_ERROR);
+ return false;
+ }
+
+ CLI::write('[setup] starting from step '.($startStep + 1).'...');
+ }
+ elseif (($startStep = $this->getSavedStartStep()) !== 0)
+ {
+ CLI::write('[setup] found firstrun progression info. (Halted on subscript: '.($this->steps[$startStep][1] ?: $this->steps[$startStep][0]).')', CLI::LOG_INFO);
+ if (!CLI::read(['x' => ['continue setup? (y/n)', true, true, '/y|n/i']], $uiN))
+ {
+ CLI::write('Failed to read answer. Use --step in a non-interactive environment.', CLI::LOG_ERROR);
+ return false;
+ }
+
+ CLI::write();
+ if (strtolower($uiN['x']) == 'n')
+ {
+ $startStep = 0;
+ CLI::write('[setup] starting from scratch...');
+ }
+ else
+ CLI::write('[setup] resuming from step '.($startStep + 1).'...');
+
+ sleep(1);
+ }
+
+
+ // init temp setup dir
+ if ($info = new \SplFileInfo(self::STEP_FILE))
+ CLISetup::writeDir($info->getPath());
+
+
+ /*******/
+ /* run */
+ /*******/
+
+ foreach ($this->steps as $idx => [$usName, , $param])
+ {
+ if ($startStep > $idx)
+ continue;
+
+ while (true)
+ {
+ CLI::write('[setup] step '.($idx + 1).' / '.count($this->steps));
+ if (CLISetup::run($usName, $param))
+ {
+ $this->saveProgress($idx);
+ break;
+ }
+
+ if (CLI::read(['x' => ['['.CLI::bold('c').']ontinue anyway? ['.CLI::bold('r').']etry? ['.CLI::bold('a').']bort?', true, true, '/c|r|a/i']], $uiCRA) && $uiCRA)
+ {
+ CLI::write();
+ switch (strtolower($uiCRA['x']))
+ {
+ case 'c':
+ $this->saveProgress($idx);
+ break 2;
+ case 'r':
+ break;
+ case 'a':
+ return false;
+ }
+ }
+ else
+ {
+ CLI::write();
+ return false;
+ }
+ }
+ }
+
+ unlink(self::STEP_FILE);
+
+ return true;
+ }
+
+ public function writeCLIHelp() : bool
+ {
+ CLI::write(' usage: php aowow --setup [--locales: --datasrc:] [--step=]', -1, false);
+ CLI::write();
+ CLI::write(' Initially essential connection information are set up and basic connectivity tests run afterwards.', -1, false);
+ CLI::write();
+ CLI::write(' In the main stage dbc and world data is compiled into the database and required sound, image and data files are generated.', -1, false);
+ CLI::write(' This should not require further input and will take about 15-20 minutes, plus 10 minutes per additional locale.', -1, false);
+ CLI::write();
+ CLI::write(' Lastly pending updates are applied and you are prompted to create an administrator account.', -1, false);
+
+ if (($startStep = $this->getSavedStartStep()) !== 0)
+ {
+ CLI::write();
+ CLI::write(' You are currently on step '.($startStep + 1).' / '.count($this->steps).' ('.($this->steps[$startStep][1] ?: $this->steps[$startStep][0]).'). You can resume or restart the setup process.', -1, false);
+ }
+
+ CLI::write();
+ CLI::write();
+
+ return true;
+ }
+
+
+ /**********/
+ /* helper */
+ /**********/
+
+ private function saveProgress (int $nStep) : void
+ {
+ if ($h = fopen(self::STEP_FILE, 'w'))
+ {
+ fwrite($h, AOWOW_REVISION."\n".($nStep + 1)."\n");
+ fclose($h);
+ }
+ else
+ CLI::write(' * UtilScript::setup - Could not access step file', CLI::LOG_ERROR);
+ }
+});
+
+?>
diff --git a/setup/tools/clisetup/siteconfig.func.php b/setup/tools/clisetup/siteconfig.func.php
deleted file mode 100644
index e1fa3239..00000000
--- a/setup/tools/clisetup/siteconfig.func.php
+++ /dev/null
@@ -1,443 +0,0 @@
- -f'));
- break;
- case 'profiler_enable':
- array_push($updScripts, 'realms', 'realmMenu');
- $fn = function($x) {
- if (!$x)
- return true;
-
- $ok = Profiler::queueStart($msg);
- if ($msg)
- CLI::write($msg, CLI::LOG_ERROR);
-
- return $ok;
- };
- break;
- case 'acc_auth_mode':
- $fn = function($x) {
- if ($x == 1 && !extension_loaded('gmp'))
- {
- CLI::write('PHP extension GMP is required to use TrinityCore as auth source, but it is currently not enabled.', CLI::LOG_ERROR);
- return false;
- }
-
- return true;
- };
- break;
- default: // nothing to do, everything is fine
- return true;
- }
-
- return $fn ? $fn($val) : true;
- };
-
- while (true)
- {
- CLI::write();
- CLI::write('select a numerical index to use the corresponding entry');
-
- $sumNum = 0;
- $cfgList = [];
- $hasEmpty = false;
- $mainBuff = [];
- $miscBuff = []; // catg 'misc' should come last
-
- foreach (Util::$configCats as $idx => $cat)
- {
- if ($idx)
- $mainBuff[] = '===== '.$cat.' =====';
- else
- $miscBuff[] = '===== '.$cat.' =====';
-
- $results = DB::Aowow()->select('SELECT *, (flags & ?d) AS php FROM ?_config WHERE `cat` = ?d ORDER BY `key` ASC', CON_FLAG_PHP, $idx);
-
- foreach ($results as $num => $data)
- {
- if (!($data['flags'] & CON_FLAG_PHP) && $data['value'] === '' && in_array($data['key'], $reqKeys))
- $hasEmpty = true;
-
- $cfgList[$sumNum + $num] = $data;
-
- $php = $data['flags'] & CON_FLAG_PHP;
- $buff = "[".CLI::bold($sumNum + $num)."] ".(($sumNum + $num) > 9 ? '' : ' ').($php ? ' PHP ' : ' AOWOW ');
- $buff .= str_pad($php ? strtolower($data['key']) : strtoupper($data['key']), 35);
- if ($data['value'] === '')
- $buff .= in_array($data['key'], $reqKeys) ? CLI::red('') : '';
- else
- {
- $info = explode(' - ', $data['comment']);
-
- if ($data['flags'] & CON_FLAG_TYPE_BOOL)
- $buff .= '[bool] '.($data['value'] ? '' : '');
- else if ($data['flags'] & CON_FLAG_OPT_LIST && !empty($info[2]))
- {
- $buff .= "[opt] ";
- foreach (explode(', ', $info[2]) as $option)
- {
- $opt = explode(':', $option);
- $buff .= '['.($data['value'] == $opt[0] ? 'x' : ' ').']'.$opt[1].' ';
- }
- }
- else if ($data['flags'] & CON_FLAG_BITMASK && !empty($info[2]))
- {
- $buff .= "[mask] ";
- foreach (explode(', ', $info[2]) as $option)
- {
- $opt = explode(':', $option);
- $buff .= '['.($data['value'] & (1 << $opt[0]) ? 'x' : ' ').']'.$opt[1].' ';
- }
- }
- else if ($data['flags'] & CON_FLAG_TYPE_STRING)
- $buff .= "[str] ".$data['value'];
- else if ($data['flags'] & CON_FLAG_TYPE_FLOAT)
- $buff .= "[float] ".floatVal($data['value']);
- else /* if ($data['flags'] & CON_FLAG_TYPE_INT) */
- $buff .= "[int] ".intVal($data['value']);
- }
-
- if ($idx)
- $mainBuff[] = $buff;
- else
- $miscBuff[] = $buff;
-
- }
-
- $sumNum += count($results);
- }
-
- foreach ($mainBuff as $b)
- CLI::write($b);
-
- foreach ($miscBuff as $b)
- CLI::write($b);
-
- CLI::write(str_pad("[".CLI::bold($sumNum)."]", 21)."add another php configuration");
-
- if ($hasEmpty)
- {
- CLI::write();
- CLI::write("please configure the required empty setings", CLI::LOG_WARN);
- }
-
- $inp = ['idx' => ['', false, '/\d/']];
- if (CLI::read($inp) && $inp && $inp['idx'] !== '')
- {
- // add new php setting
- if ($inp['idx'] == $sumNum)
- {
- CLI::write();
- CLI::write("Adding additional php configuration.");
-
- while (true)
- {
- $setting = array(
- 'key' => ['option name', false, '/[\w_\.\-]/i'],
- 'val' => ['value', ]
- );
- if (CLI::read($setting) && $setting)
- {
- CLI::write();
-
- $key = strtolower($setting['key']);
- if (ini_get($key) === false || ini_set($key, $setting['val']) === false)
- {
- CLI::write("this configuration option cannot be set", CLI::LOG_ERROR);
- sleep(1);
- }
- else if (DB::Aowow()->selectCell('SELECT 1 FROM ?_config WHERE `flags` & ?d AND `key` = ?', CON_FLAG_PHP, $key))
- {
- CLI::write("this configuration option is already in use", CLI::LOG_ERROR);
- sleep(1);
- }
- else
- {
- DB::Aowow()->query('INSERT IGNORE INTO ?_config (`key`, `value`, `cat`, `flags`) VALUES (?, ?, 0, ?d)', $key, $setting['val'], CON_FLAG_TYPE_STRING | CON_FLAG_PHP);
- CLI::write("new php configuration added", CLI::LOG_OK);
- sleep(1);
- }
-
- break;
- }
- else
- {
- CLI::write();
- CLI::write("edit canceled! returning to list...", CLI::LOG_INFO);
- sleep(1);
- break;
- }
- }
- }
- // edit existing setting
- else if ($inp['idx'] >= 0 && $inp['idx'] < $sumNum)
- {
- $conf = $cfgList[$inp['idx']];
- $info = explode(' - ', $conf['comment']);
- $key = strtolower($conf['key']);
- $buff = '';
-
- CLI::write();
- $buff .= $conf['flags'] & CON_FLAG_PHP ? " PHP: " : "AOWOW: ";
- $buff .= $conf['flags'] & CON_FLAG_PHP ? $key : strtoupper('cfg_'.$conf['key']);
-
- if (!empty($info[1]))
- $buff .= " - ".$info[1];
-
- CLI::write($buff);
-
- $buff = "VALUE: ";
-
- if ($conf['flags'] & CON_FLAG_TYPE_BOOL)
- $buff .= $conf['value'] ? '' : '';
- else if ($conf['flags'] & CON_FLAG_OPT_LIST && !empty($info[2]))
- {
- foreach (explode(', ', $info[2]) as $option)
- {
- $opt = explode(':', $option);
- $buff .= '['.($conf['value'] == $opt[0] ? 'x' : ' ').'] '.$opt[1].' ';
- }
- }
- else if ($conf['flags'] & CON_FLAG_BITMASK && !empty($info[2]))
- {
- foreach (explode(', ', $info[2]) as $option)
- {
- $opt = explode(':', $option);
- $buff .= '['.($conf['value'] & (1 << $opt[0]) ? 'x' : ' ').'] '.$opt[1].' ';
- }
- }
- else if ($conf['flags'] & CON_FLAG_TYPE_STRING)
- $buff .= $conf['value'];
- else if ($conf['flags'] & CON_FLAG_TYPE_FLOAT)
- $buff .= floatVal($conf['value']);
- else /* if ($conf['flags'] & CON_FLAG_TYPE_INT) */
- $buff .= intVal($conf['value']);
-
- CLI::write($buff);
- CLI::write();
- CLI::write("[".CLI::bold('E')."]dit");
-
- if (!($conf['flags'] & CON_FLAG_PERSISTENT))
- CLI::write("[".CLI::bold('D')."]elete");
-
- if (strstr($info[0], 'default:'))
- CLI::write("[".CLI::bold('R')."]estore Default - ".trim(explode('default:', $info[0])[1]));
-
- while (true)
- {
- $action = ['idx' => ['', true, '/[edr]/i']];
- if (CLI::read($action, true) && $action)
- {
- switch (strtoupper($action['idx']))
- {
- case 'E': // edit value
- $pattern = false;
- $single = false;
- $value = ['idx' => ['Select new value', false, &$pattern]];
-
- if ($conf['flags'] & CON_FLAG_OPT_LIST)
- {
- $_valid = [];
- foreach (explode(', ', $info[2]) as $option)
- {
- $opt = explode(':', $option);
- $_valid[] = $opt[0];
- CLI::write('['.CLI::bold($opt[0]).'] '.$opt[1]);
- }
- $single = true;
- $pattern = '/\d/';
- $validate = function ($v) use($_valid) { return in_array($v, $_valid); };
- }
- else if ($conf['flags'] & CON_FLAG_BITMASK)
- {
- CLI::write('Bitmask: sum fields to select multiple options');
- $_valid = 0x0;
- foreach (explode(', ', $info[2]) as $option)
- {
- $opt = explode(':', $option);
- $_valid |= (1 << $opt[0]);
- CLI::write('['.CLI::bold(1 << $opt[0]).']'.str_pad('', 4-strlen(1 << $opt[0])).$opt[1]);
- }
- $pattern = '/\d+/';
- $validate = function ($v) use($_valid) { $v = $v & $_valid; return $v; };
- }
- else if ($conf['flags'] & CON_FLAG_TYPE_BOOL)
- {
- CLI::write('['.CLI::bold(0).'] Disabled');
- CLI::write('['.CLI::bold(1).'] Enabled');
-
- $single = true;
- $pattern = '/[01]/';
- $validate = function ($v) { return true; };
- }
- else if ($conf['flags'] & CON_FLAG_TYPE_INT)
- $validate = function ($v) { return preg_match('/^-?\d+$/i', $v); };
- else if ($conf['flags'] & CON_FLAG_TYPE_FLOAT)
- $validate = function ($v) { return preg_match('/^-?\d*(,|.)?\d+$/i', $v); };
- else // string
- $validate = function ($v) { return true; };
-
-
- while (true)
- {
- $use = $value;
- if (CLI::read($use, $single))
- {
- CLI::write();
-
- $inp = $use['idx'] ?? '';
- if (!$validate($inp))
- {
- CLI::write("value not in range", CLI::LOG_ERROR);
- sleep(1);
- continue;
- }
- else
- {
- $oldVal = DB::Aowow()->selectCell('SELECT `value` FROM ?_config WHERE `key` = ?', $key);
- DB::Aowow()->query('UPDATE ?_config SET `value` = ? WHERE `key` = ?', $inp, $key);
-
- // postChange returned false => reset value
- if (!$onChange($key, $inp))
- {
- DB::Aowow()->query('UPDATE ?_config SET `value` = ? WHERE `key` = ?', $oldVal, $key);
- sleep(1);
- break;
- }
-
- CLI::write("setting updated", CLI::LOG_OK);
- sleep(1);
- break 3;
- }
- }
- else
- {
- CLI::write("edit canceled! returning to selection...", CLI::LOG_INFO);
- sleep(1);
- break;
- }
- }
-
- break 2;
- case 'R': // restore default
- if (!strstr($info[0], 'default:'))
- continue 2;
-
- // @eval .. some dafault values are supplied as bitmask or the likes
- $val = trim(explode('default:', $info[0])[1]);
- if (!($conf['flags'] & CON_FLAG_TYPE_STRING))
- $val = @eval('return ('.$val.');');
- if (DB::Aowow()->query('UPDATE ?_config SET `value` = ? WHERE `key` = ?', $val, $key))
- {
- CLI::write("default value restored", CLI::LOG_OK);
- $onChange($key, $val);
- sleep(1);
- }
- break 2;
- case 'D': // delete config pair
- if ($conf['flags'] & CON_FLAG_PERSISTENT)
- continue 2;
-
- if (DB::Aowow()->query('DELETE FROM ?_config WHERE `key` = ? AND (`flags` & ?d) = 0', $key, CON_FLAG_PERSISTENT))
- {
- CLI::write("php setting deleted ['".$conf['key']."': '".$conf['value']."']", CLI::LOG_OK);
- sleep(1);
- }
- break 2;
- }
- }
- else
- {
- CLI::write();
- CLI::write('edit canceled! returning to list...', CLI::LOG_INFO);
- sleep(1);
- break;
- }
- }
- }
- else
- {
- CLI::write();
- CLI::write('invalid selection', CLI::LOG_ERROR);
- sleep(1);
- }
- }
- else
- {
- CLI::write();
- CLI::write('leaving site configuration...', CLI::LOG_INFO);
- break;
- }
-
- // propagate changes to static files
- if ($updScripts && (!class_exists('FileGen') || FileGen::getMode() != FileGen::MODE_FIRSTRUN))
- {
- require_once 'setup/tools/clisetup/build.func.php';
- CLI::write();
- CLI::write('regenerating affected static content', CLI::LOG_INFO);
- CLI::write();
- sleep(1);
-
- if ($_ = array_diff($updScripts, build($updScripts)))
- {
- CLI::write(' - the following updates returned with errors, please recheck those - '.implode(', ', $_), CLI::LOG_ERROR);
- sleep(1);
- }
-
- sleep(1);
- $updScripts = [];
- }
- }
-
- // actually load set constants
- loadConfig(true);
-}
-
-?>
diff --git a/setup/tools/clisetup/siteconfig.us.php b/setup/tools/clisetup/siteconfig.us.php
new file mode 100644
index 00000000..25b8247a
--- /dev/null
+++ b/setup/tools/clisetup/siteconfig.us.php
@@ -0,0 +1,478 @@
+ cfgName [newValue]]';
+ public const DESCRIPTION = 'Configure site variables.';
+ public const PROMPT = 'SITE_HOST and STATIC_HOST *must* be set. Also enable FORCE_SSL if needed. You may also want to change other variables such as NAME, NAME_SHORT or LOCALES.';
+ public const NOTE_ERROR = 'could not access:';
+
+ public const REQUIRED_DB = [DB_AOWOW];
+
+ public const USE_CLI_ARGS = true;
+
+ private const HTTP_STATUS_OK = 200;
+ private const HTTP_STATUS_MOVED_PERM = 301;
+ private const HTTP_STATUS_MOVED_TEMP = 302;
+
+ private $updScripts = [];
+
+ // args: action, configName, configValue, pendingUpdates[] // iiio
+ public function run(&$args) : bool
+ {
+ $action = $args[0] ?? '';
+ $name = $args[1] ?? '';
+ $value = $args[2] ?? '';
+
+ $result = true;
+ switch (strtoupper($action))
+ {
+ case 'E':
+ $result = $this->doEdit($name, $args[2]);
+ break;
+ case 'R':
+ $result = $this->doRestore($name);
+ break;
+ case 'N':
+ $result = $this->doNew($name, $value);
+ break;
+ case 'D':
+ $result = $this->doDelete($name);
+ break;
+ default:
+ $this->showConfigList();
+ }
+
+ $args['doBuild'] = $this->updScripts; // push files to rebuild one level up
+
+ return $result;
+ }
+
+ private function showConfigList() : void
+ {
+ while (true)
+ {
+ CLI::write('select a numerical index or name to use the corresponding entry', -1, false);
+ CLI::write();
+
+ $sumNum = 0;
+ $cfgList = [];
+ $hasEmpty = false;
+ $listBuff = [];
+
+ foreach (Cfg::$categories as $idx => $cat)
+ {
+ $listBuff[] = '===== '.$cat.' =====';
+
+ foreach (Cfg::forCategory($idx) as $key => [$value, $flags, $catg, $default, $comment])
+ {
+ $isPhp = $flags & Cfg::FLAG_PHP;
+
+ if ($value === '' && ($flags & Cfg::FLAG_REQUIRED))
+ $hasEmpty = true;
+
+ $cfgList[$sumNum] = strtolower($key);
+
+ $row = '['.CLI::bold($sumNum).'] '.(($sumNum) > 9 ? '' : ' ').($isPhp ? ' PHP ' : ' AOWOW ');
+ $row .= str_pad($isPhp ? strtolower($key) : strtoupper($key), 35);
+
+ $opts = explode(' - ', $comment);
+ $row .= $this->formatValue($value, $flags, $opts[1] ?? '');
+
+ $listBuff[] = $row;
+ $sumNum++;
+ }
+ }
+
+ foreach ($listBuff as $b)
+ CLI::write($b, -1, false);
+
+ CLI::write(str_pad('['.CLI::bold($sumNum).']', 21).'add another php configuration', -1, false);
+ CLI::write();
+
+ if ($hasEmpty)
+ {
+ CLI::write('please configure the required empty settings', CLI::LOG_WARN);
+ CLI::write();
+ }
+
+ if (CLI::read(['idx' => ['', false, false, Cfg::PATTERN_CONF_KEY_CHAR]], $uiIndex) && $uiIndex && $uiIndex['idx'] !== '')
+ {
+ $idx = array_search(strtolower($uiIndex['idx']), $cfgList);
+ if ($idx === false)
+ $idx = intVal($uiIndex['idx']);
+
+ // add new php setting
+ if ($idx == $sumNum)
+ $this->showNewConfig();
+ // edit existing setting
+ else if ($idx >= 0 && $idx < $sumNum)
+ $this->showEditConfig($cfgList[$idx] ?? '');
+ else
+ CLI::write('invalid selection', CLI::LOG_ERROR);
+
+ CLI::write();
+ sleep(1);
+ }
+ else
+ {
+ CLI::write('leaving site configuration...', CLI::LOG_INFO);
+ CLI::write();
+ break;
+ }
+ }
+ }
+
+ private function showNewConfig() : void
+ {
+ CLI::write('Adding additional php configuration.');
+ CLI::write();
+
+ $setting = array(
+ 'key' => ['option name', false, false, Cfg::PATTERN_CONF_KEY_CHAR],
+ 'val' => ['value']
+ );
+ if (CLI::read($setting, $uiSetting) && $uiSetting)
+ $this->doNew($uiSetting['key'], $uiSetting['val']);
+ else
+ CLI::write('edit canceled! returning to list...', CLI::LOG_INFO);
+ }
+
+ private function showEditConfig(string $key) : void
+ {
+ [$value, $flags, , $default, $comment] = Cfg::get($key, false, true);
+ $info = explode(' - ', $comment);
+ $buff = '';
+
+ $buff .= $flags & Cfg::FLAG_PHP ? 'PHP: ' : 'AOWOW: ';
+ $buff .= $flags & Cfg::FLAG_PHP ? $key : 'Cfg::'.strtoupper($key);
+
+ if (!empty($info[0]))
+ $buff .= ' - '.$info[0];
+
+ CLI::write($buff);
+ CLI::write();
+
+ CLI::write('VALUE: '.$this->formatValue($value, $flags, $info[1] ?? ''));
+ CLI::write();
+ CLI::write('['.CLI::bold('E').']dit');
+
+ if (!($flags & Cfg::FLAG_PERSISTENT))
+ CLI::write('['.CLI::bold('D').']elete');
+
+ if ($default)
+ CLI::write('['.CLI::bold('R').']estore Default - '.$default);
+
+ CLI::write();
+
+ while (true)
+ {
+ CLI::write();
+ sleep(1);
+
+ if (CLI::read(['idx' => ['', true, true, '/[edr]/i']], $uiEDR) && $uiEDR)
+ {
+ switch (strtoupper($uiEDR['idx']))
+ {
+ case 'E': // edit value
+ if (!$this->doEdit($key))
+ continue 2;
+ break 2;
+ case 'R': // restore default
+ if (!$this->doRestore($key))
+ continue 2;
+ break 2;
+ case 'D': // delete config pair
+ if (!$this->doDelete($key))
+ continue 2;
+ break 2;
+ }
+ }
+ else
+ {
+ CLI::write('edit canceled! returning to list...', CLI::LOG_INFO);
+ break;
+ }
+ }
+ }
+
+ private function doEdit(string $key, ?string $newVal = null) : bool
+ {
+ [, $flags, , , $comment] = Cfg::get($key, false, true);
+ $info = explode(' - ', $comment);
+
+ $pattern = '/.*/';
+ $single = false;
+ $typeHint = [];
+
+ if ($flags & Cfg::FLAG_OPT_LIST)
+ {
+ foreach (explode(', ', $info[1]) as $option)
+ {
+ [$val, $name] = explode(':', $option);
+ $typeHint[] = '['.CLI::bold($val).'] '.$name;
+ }
+ $single = true;
+ $pattern = '/^\d$/';
+ }
+ else if ($flags & Cfg::FLAG_BITMASK)
+ {
+ foreach (explode(', ', $info[1]) as $option)
+ {
+ [$val, $name] = explode(':', $option);
+ $typeHint[] = '['.CLI::bold(1 << $val).']'.str_pad('', 6 - strlen(1 << $val)).$name;
+ }
+ $typeHint[] = 'Bitmask: sum fields to select multiple options';
+ $pattern = '/^\d+$/';
+ }
+ else if ($flags & Cfg::FLAG_TYPE_BOOL)
+ {
+ $typeHint[] = '['.CLI::bold(0).'] Disabled';
+ $typeHint[] = '['.CLI::bold(1).'] Enabled';
+
+ $single = true;
+ $pattern = '/^[01]$/';
+ }
+ else if ($flags & Cfg::FLAG_TYPE_INT)
+ $pattern = '/^-?\d+$/';
+ else if ($flags & Cfg::FLAG_TYPE_FLOAT)
+ $pattern = '/^-?\d*(,|.)?\d+$/i';
+
+ while (true)
+ {
+ if (!isset($newVal))
+ foreach ($typeHint as $th)
+ CLI::write($th);
+
+ if ((isset($newVal) && preg_match($pattern, $newVal)) || CLI::read(['idx' => ['Select new value', false, $single, $pattern]], $uiValue))
+ {
+ CLI::write();
+
+ $val = $newVal ?? $uiValue['idx'] ?? '';
+ unset($newVal); // we loop infinitely if this is set and invalid
+
+ if ($err = Cfg::set($key, $val, $this->updScripts))
+ {
+ CLI::write($err, CLI::LOG_ERROR);
+ continue;
+ }
+ else
+ {
+ CLI::write('setting updated', CLI::LOG_OK);
+ return true;
+ }
+ }
+ else
+ {
+ CLI::write('edit canceled! returning to selection...', CLI::LOG_INFO);
+ return false;
+ }
+ }
+ }
+
+ private function doRestore(string $key) : bool
+ {
+ if ($err = Cfg::reset($key, $this->updScripts))
+ {
+ CLI::write($err, CLI::LOG_ERROR);
+ return false;
+ }
+
+ CLI::write('default value restored', CLI::LOG_OK);
+ return true;
+ }
+
+ private function doNew(string $key, string $val) : bool
+ {
+ if ($err = Cfg::add($key, $val))
+ {
+ CLI::write($err, CLI::LOG_ERROR);
+ return false;
+ }
+
+ CLI::write('new php configuration added', CLI::LOG_OK);
+ return true;
+ }
+
+ private function doDelete(string $key) : bool
+ {
+ if ($err = Cfg::delete($key))
+ {
+ CLI::write($err, CLI::LOG_ERROR);
+ return false;
+ }
+
+ CLI::write('php setting deleted: '.$key, CLI::LOG_OK);
+ return true;
+ }
+
+
+ /******************/
+ /* Unit formating */
+ /******************/
+
+ private function toOptList(string $options, $curVal, bool $bitmask = false) : string
+ {
+ $result = '';
+ foreach (explode(', ', $options) as $opt)
+ {
+ [$val, $name] = explode(':', $opt);
+ $equal = $bitmask ? ($curVal & (1 << $val)) : $curVal == $val;
+
+ $result .= '['.($equal ? 'x' : ' ').']'.$name.' ';
+ }
+
+ return substr($result, 0, -1);
+ }
+
+ private function formatValue($value, int $flags, string $opts) : string
+ {
+ if ($flags & Cfg::FLAG_TYPE_BOOL)
+ return '[bool] '.($value ? '' : '');
+
+ if ($flags & Cfg::FLAG_OPT_LIST)
+ return '[opt] '.$this->toOptList($opts, $value, false);
+
+ if ($flags & Cfg::FLAG_BITMASK)
+ return '[mask] '.$this->toOptList($opts, $value, true);
+
+ if ($flags & Cfg::FLAG_TYPE_FLOAT)
+ return '[float] '.floatVal($value);
+
+ if ($flags & Cfg::FLAG_TYPE_INT)
+ return '[int] '.intVal($value);
+
+ // if ($flags & Cfg::FLAG_TYPE_STRING)
+ if ($value === '')
+ return '[str] '.(($flags & Cfg::FLAG_REQUIRED) ? CLI::red('') : CLI::grey(''));
+ else
+ return '[str] "'.$value.'"';
+ }
+
+
+ /****************/
+ /* Help display */
+ /****************/
+
+ public function writeCLIHelp(string ...$ss) : bool
+ {
+ CLI::write(' usage: php aowow --configure [action cfgName [newValue]]', -1, false);
+ CLI::write();
+ CLI::write(' Configures site and php variables. If incomplete parameters are passed an interactive prompt will open.', -1, false);
+ CLI::write();
+ CLI::write(' action:', -1, false);
+ CLI::write(' E - Edit variable named '.CLI::bold('cfgName').'. Optionally directly pass a new value.', -1, false);
+ CLI::write(' R - Restore default value of a variable '.CLI::bold('cfgName').'.', -1, false);
+ CLI::write(' N - Create a new php config value. Must be a valid php directive. Optionally directly pass a new value.', -1, false);
+ CLI::write(' D - Delete an existing php config value.', -1, false);
+ CLI::write();
+ CLI::write();
+
+ return true;
+ }
+
+
+ /***********/
+ /* DB test */
+ /***********/
+
+ public function test(?array &$error = []) : bool
+ {
+ $error = [];
+
+ if (!DB::isConnected(DB_AOWOW))
+ {
+ $error[] = ' * not connected to DB';
+ return false;
+ }
+
+ $prot = Cfg::get('FORCE_SSL') ? 'https://' : 'http://';
+ $cases = array(
+ 'site_host' => [$prot, Cfg::get('SITE_HOST'), '/index.php'],
+ 'static_host' => [$prot, Cfg::get('STATIC_HOST'), '/css/aowow.css']
+ );
+
+ foreach ($cases as $conf => [$protocol, $host, $testFile])
+ {
+ if ($host)
+ {
+ $resp = 0;
+ if (!$this->testCase($protocol, $host, $testFile, $resp))
+ {
+ if ($resp == self::HTTP_STATUS_MOVED_PERM || $resp == self::HTTP_STATUS_MOVED_TEMP)
+ {
+ CLI::write('self test received status '.CLI::bold($resp).' (page moved) for '.$conf.', pointing to: '.$protocol.$host.$testFile, CLI::LOG_WARN);
+ if (!CLI::read(['x' => ['should '.CLI::bold($conf).' be set to '.CLI::bold($host).' and force_ssl be updated? (y/n)', true, true, '/y|n/i']], $uiN) || !$uiN || strtolower($uiN['x']) == 'n')
+ $error[] = ' * '.$protocol.$host.$testFile.' ['.$resp.']';
+ else
+ {
+ Cfg::set($conf, $host);
+ Cfg::set('FORCE_SSL', $protocol == 'https://');
+ }
+
+ CLI::write();
+ }
+ else
+ $error[] = ' * '.$protocol.$host.$testFile.' ['.$resp.']';
+ }
+ }
+ else
+ $error[] = ' * '.strtoupper($conf).' is empty';
+ }
+
+ return empty($error);
+ }
+
+ private function testCase(&$protocol, &$host, $testFile, &$status) : bool
+ {
+ // https://stackoverflow.com/questions/14279095/allow-self-signed-certificates-for-https-wrapper
+ $ctx = stream_context_create(array(
+ 'ssl' => ['verify_peer' => false,
+ 'allow_self_signed' => true]
+ ));
+
+ $res = get_headers($protocol.$host.$testFile, true, $ctx);
+
+ if (!$res || !preg_match('/HTTP\/[0-9\.]+\s+([0-9]+)/', $res[0], $m))
+ return false;
+
+ $status = $m[1];
+
+ if ($status == self::HTTP_STATUS_OK)
+ return true;
+
+ if ($status == self::HTTP_STATUS_MOVED_PERM || $status == self::HTTP_STATUS_MOVED_TEMP)
+ {
+ if (!empty($res['Location']) && preg_match('/(https?:\/\/)(.*)'.strtr($testFile, ['/' => '\/', '.' => '\.']).'/i', is_array($res['Location']) ? $res['Location'][0] : $res['Location'], $n))
+ {
+ $protocol = $n[1];
+ $host = $n[2];
+ }
+
+ return false;
+ }
+
+ $status = 0;
+ return false;
+ }
+});
+
+?>
diff --git a/setup/tools/clisetup/sql.func.php b/setup/tools/clisetup/sql.func.php
deleted file mode 100644
index 052f6c7c..00000000
--- a/setup/tools/clisetup/sql.func.php
+++ /dev/null
@@ -1,60 +0,0 @@
-
diff --git a/setup/tools/clisetup/sync.func.php b/setup/tools/clisetup/sync.func.php
deleted file mode 100644
index 388d4c7f..00000000
--- a/setup/tools/clisetup/sync.func.php
+++ /dev/null
@@ -1,51 +0,0 @@
- [--locales: --mpqDataDir: --force -f]', -1, false);
- CLI::write();
- CLI::write(' Truncates and recreates AoWoW tables and static data files that depend on the given TC world table. Use this command after you updated your world database.', -1, false);
- CLI::write();
- CLI::write(' e.g.: "php aowow --sync=creature_queststarter" causes the table aowow_quests_startend to be recreated.', -1, false);
- CLI::write(' Also quest-related profiler files will be recreated as they depend on aowow_quests_startend and thus indirectly on creature_queststarter.', -1, false);
- CLI::write();
- return;
- }
-
- $_s = sql($s);
- if ($s)
- {
- $_ = array_diff($s, $_s);
- DB::Aowow()->query('UPDATE ?_dbversion SET `sql` = ?', $_ ? implode(' ', $_) : '');
- }
-
- $_b = build($b);
- if ($b)
- {
- $_ = array_diff($b, $_b);
- DB::Aowow()->query('UPDATE ?_dbversion SET `build` = ?', $_ ? implode(' ', $_) : '');
- }
-
- if (!$s && !$_s && !$b && !$_b)
- CLI::write('no valid table names supplied', CLI::LOG_ERROR);
-}
-
-?>
diff --git a/setup/tools/clisetup/sync.us.php b/setup/tools/clisetup/sync.us.php
new file mode 100644
index 00000000..38d20d81
--- /dev/null
+++ b/setup/tools/clisetup/sync.us.php
@@ -0,0 +1,106 @@
+';
+
+ public const REQUIRED_DB = [DB_AOWOW, DB_WORLD];
+
+ // sqlToDo, buildToDo, null, null // iinn
+ public function run(&$args) : bool
+ {
+ $s = &$args['doSql'];
+ $b = &$args['doBuild'];
+
+ // called manually
+ if ($s === null && $b === null)
+ {
+ [$s, $b] = $this->handleCLIOpt();
+ if (!$s && !$b && !CLISetup::getOpt('setup'))
+ {
+ CLI::write('[sync] no valid table names supplied', CLI::LOG_ERROR);
+ return false;
+ }
+ }
+
+ if ($s)
+ {
+ $io = ['doSql' => $s, 'doneSql' => []];
+ CLISetup::run('sql', $io);
+ DB::Aowow()->qry('UPDATE ::dbversion SET `sql` = %s', implode(' ', array_diff($io['doSql'], $io['doneSql'])));
+ }
+
+ if ($b)
+ {
+ $io = ['doBuild' => $b, 'doneBuild' => []];
+ CLISetup::run('build', $io);
+ DB::Aowow()->qry('UPDATE ::dbversion SET `build` = %s', implode(' ', array_diff($io['doBuild'], $io['doneBuild'])));
+ }
+
+ return true;
+ }
+
+ private function handleCLIOpt() : array
+ {
+ $sql = [];
+ $build = [];
+
+ $sync = CLISetup::getOpt('sync');
+ if (!$sync)
+ return [$sql, $build];
+
+ foreach (CLISetup::getSubScripts() as $name => [$invoker, $ssRef])
+ if (array_intersect($ssRef->getRemoteDependencies(), $sync))
+ $$invoker[] = $name;
+
+ do
+ {
+ $n = count($sql);
+ foreach (CLISetup::getSubScripts('sql') as $name => [, $ssRef])
+ if (!in_array($name, $sql) && array_intersect($ssRef->getSelfDependencies()[0], $sql))
+ $sql[] = $name;
+ }
+ while ($n != count($sql));
+
+ if ($sql)
+ foreach (CLISetup::getSubScripts('build') as $name => [, $ssRef])
+ if (array_intersect($ssRef->getSelfDependencies()[0], $sql))
+ $build[] = $name;
+
+ return [array_unique($sql), array_unique($build)];
+ }
+
+ public function writeCLIHelp() : bool
+ {
+ CLI::write(' usage: php aowow --sync= [--locales: --datasrc: --force -f]', -1, false);
+ CLI::write();
+ CLI::write(' Truncates and recreates AoWoW tables and static data files that depend on the given TC world table. Use this command after you updated your world database.', -1, false);
+ CLI::write();
+ CLI::write(' e.g.: "php aowow --sync=creature_queststarter" causes the table aowow_quests_startend to be recreated.', -1, false);
+ CLI::write(' Also quest-related profiler files will be recreated as they depend on aowow_quests_startend and thus indirectly on creature_queststarter.', -1, false);
+ CLI::write();
+ CLI::write();
+
+ return true;
+ }
+})
+
+?>
diff --git a/setup/tools/clisetup/update.func.php b/setup/tools/clisetup/update.func.php
deleted file mode 100644
index d61d72b4..00000000
--- a/setup/tools/clisetup/update.func.php
+++ /dev/null
@@ -1,91 +0,0 @@
-selectRow('SELECT `date`, `part` FROM ?_dbversion'));
-
- if (CLISetup::getOpt('help'))
- {
- CLI::write();
- CLI::write(' usage: php aowow --update', -1, false);
- CLI::write();
- CLI::write(' Checks /setup/updates for new *.sql files and applies them. If required by an applied update, the --sql and --build command are triggered afterwards.', -1, false);
- CLI::write(' Use this after fetching the latest rev. from Github.', -1, false);
- CLI::write();
- CLI::write(' Last Update: '.date(Util::$dateFormatInternal, $date).' (Part #'.$part.')', -1, false);
- CLI::write();
- return;
- }
-
- CLI::write('checking sql updates');
- CLISetup::siteLock(CLISetup::LOCK_ON);
-
- $nFiles = 0;
- foreach (glob('setup/updates/*.sql') as $file)
- {
- $pi = pathinfo($file);
- [$fDate, $fPart] = explode('_', $pi['filename']);
-
- $fData = intVal($fDate);
-
- if ($date && $fDate < $date)
- continue;
- else if ($part && $date && $fDate == $date && $fPart <= $part)
- continue;
-
- $nFiles++;
-
- $updQuery = '';
- $nQuerys = 0;
- foreach (file($file) as $line)
- {
- // skip comments
- if (substr($line, 0, 2) == '--' || $line == '')
- continue;
-
- $updQuery .= $line;
-
- // semicolon at the end -> end of query
- if (substr(trim($line), -1, 1) == ';')
- {
- if (DB::Aowow()->query($updQuery))
- $nQuerys++;
-
- $updQuery = '';
- }
- }
-
- DB::Aowow()->query('UPDATE ?_dbversion SET `date`= ?d, `part` = ?d', $fDate, $fPart);
- CLI::write(' -> '.date('d.m.Y', $fDate).' #'.$fPart.': '.$nQuerys.' queries applied', CLI::LOG_OK);
- }
-
- CLISetup::siteLock(CLISetup::LOCK_RESTORE);
- CLI::write($nFiles ? 'applied '.$nFiles.' update(s)' : 'db is already up to date', CLI::LOG_OK);
-
- // fetch sql/build after applying updates, as they may contain sync-prompts
- [$sql, $build] = array_values(DB::Aowow()->selectRow('SELECT `sql`, `build` FROM ?_dbversion'));
-
- sleep(1);
-
- $sql = trim($sql) ? array_unique(explode(' ', trim(preg_replace('/[^a-z]+/i', ' ', $sql)))) : [];
- $build = trim($build) ? array_unique(explode(' ', trim(preg_replace('/[^a-z]+/i', ' ', $build)))) : [];
-
- if ($sql)
- CLI::write('The following table(s) require syncing: '.implode(', ', $sql));
-
- if ($build)
- CLI::write('The following file(s) require syncing: '.implode(', ', $build));
-}
-
-?>
diff --git a/setup/tools/clisetup/update.us.php b/setup/tools/clisetup/update.us.php
new file mode 100644
index 00000000..9b4cc185
--- /dev/null
+++ b/setup/tools/clisetup/update.us.php
@@ -0,0 +1,128 @@
+date, $this->part] = array_values(DB::Aowow()->selectRow('SELECT `date`, `part` FROM ::dbversion'));
+ }
+
+ // args: null, null, sqlToDo, buildToDo // nnoo
+ public function run(&$args) : bool
+ {
+ $sql = &$args['doSql'];
+ $build = &$args['doBuild'];
+
+ CLI::write('[update] checking for sql updates...');
+
+ $nFiles = 0;
+ foreach (glob('setup/sql/updates/*.sql') as $file)
+ {
+ $pi = pathinfo($file);
+
+ // invalid file
+ if (!preg_match('/(\d{10})_(\d{2})/', $pi['filename'], $m))
+ continue;
+
+ $fDate = intVal($m[1]);
+ $fPart = intVal($m[2]);
+
+ if ($this->date && $fDate < $this->date)
+ continue;
+ else if ($this->part && $this->date && $fDate == $this->date && $fPart <= $this->part)
+ continue;
+
+ $nFiles++;
+
+ $updQuery = '';
+ $nQuerys = 0;
+ foreach (file($file) as $line)
+ {
+ // skip comments
+ if (substr($line, 0, 2) == '--' || $line == '')
+ continue;
+
+ $updQuery .= $line;
+
+ // semicolon at the end -> end of query
+ if (substr(trim($line), -1, 1) == ';')
+ {
+ if (DB::Aowow()->qry($updQuery))
+ $nQuerys++;
+
+ $updQuery = '';
+ }
+ }
+
+ DB::Aowow()->qry('UPDATE ::dbversion SET `date`= %i, `part` = %i', $fDate, $fPart);
+ CLI::write(' -> '.date('d.m.Y', $fDate).' #'.$fPart.': '.$nQuerys.' queries applied', CLI::LOG_OK);
+ }
+
+ CLI::write('[update] ' . ($nFiles ? 'applied '.$nFiles.' update(s)' : 'db is already up to date'), CLI::LOG_OK);
+
+ // fetch sql/build after applying updates, as they may contain sync-prompts
+ [$sql, $build] = DB::Aowow()->selectRow('SELECT `sql` AS "0", `build` AS "1" FROM ::dbversion');
+
+ $sql = trim($sql) ? array_unique(explode(' ', trim(preg_replace('/[^a-z_\-]+/i', ' ', $sql)))) : [];
+ $build = trim($build) ? array_unique(explode(' ', trim(preg_replace('/[^a-z_\-]+/i', ' ', $build)))) : [];
+
+ sleep(1);
+
+ if ($sql)
+ CLI::write('[update] The following sql scripts have been scheduled: '.implode(', ', $sql));
+
+ if ($build)
+ CLI::write('[update] The following build scripts have been scheduled: '.implode(', ', $build));
+
+ return true;
+ }
+
+ public function writeCLIHelp() : bool
+ {
+ CLI::write(' usage: php aowow --update', -1, false);
+ CLI::write();
+ CLI::write(' Checks /setup/sql/updates for new *.sql files and applies them. If required by an applied update, the --sql and --build command are triggered afterwards.', -1, false);
+ CLI::write(' Use this after fetching the latest rev. from Github.', -1, false);
+
+ if ($this->date)
+ {
+ CLI::write();
+ CLI::write(' Last Update: '.date(Util::$dateFormatInternal, $this->date).' (Part #'.$this->part.')', -1, false);
+ }
+
+ CLI::write();
+ CLI::write();
+
+ return true;
+ }
+});
+
+?>
diff --git a/setup/tools/dbc.class.php b/setup/tools/dbc.class.php
deleted file mode 100644
index 740e212a..00000000
--- a/setup/tools/dbc.class.php
+++ /dev/null
@@ -1,635 +0,0 @@
-
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU Affero General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU Affero General Public License for more details.
-
- You should have received a copy of the GNU Affero General Public License
- along with this program. If not, see .
-*/
-
-if (!defined('AOWOW_REVISION'))
- die('illegal access');
-
-if (!CLI)
- die('not in cli mode');
-
-
-/*
- Supported format characters:
- x - not used/unknown, 4 bytes
- X - not used/unknown, 1 byte
- s - char*
- f - float, 4 bytes (rounded to 4 digits after comma)
- u - unsigned int, 4 bytes
- i - signed int, 4 bytes
- b - unsigned char, 1 byte
- d - sorted by this field, not included in array
- n - same, but field included in array
-*/
-class DBC
-{
- private $_formats = array( // locales block for copy pasta: sxsssxsxsxxxxxxxx | xxxxxxxxxxxxxxxxx
- 'achievement' => 'niiisxsssxsxsxxxxxxxxsxsssxsxsxxxxxxxxiiiiisxsssxsxsxxxxxxxxii',
- 'achievement_category' => 'nixxxxxxxxxxxxxxxxxx',
- 'achievement_criteria' => 'niiiiiiiisxsssxsxsxxxxxxxxiixii',
- 'areatable' => 'niixixxiiixsxsssxsxsxxxxxxxxixxxxxxx',
- 'areatrigger' => 'niffxxxxxf',
- 'battlemasterlist' => 'niixxxxxxixxxxxxxxxxxxxxxxxxixii',
- 'charbaseinfo' => 'bb',
- 'charstartoutfit' => 'nbbbXiiiiiiiiiiiiiiiiiiiixxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',
- 'chartitles' => 'nxsxsssxsxsxxxxxxxxsxsssxsxsxxxxxxxxi',
- 'chrclasses' => 'nxixsxsssxsxsxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxsxixi',
- 'chrraces' => 'niixxxxixxxsxisxsssxsxsxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxi',
- 'creaturedisplayinfo' => 'niiixxssssxxixxx',
- 'creaturedisplayinfoextra' => 'nxxxxxxxxxxxxxxxxxxxs',
- 'creaturefamily' => 'nxxxxixiiisxsssxsxsxxxxxxxxs',
- 'creaturemodeldata' => 'nxxxxxxxxxxxxixxxxxxxxxxxxxx',
- 'creaturesounddata' => 'niiiixiiiiiiiiixxxxixxxxixiiiiixxiiiix',
- 'currencytypes' => 'niix',
- 'dungeonmap' => 'niiffffi',
- 'durabilitycosts' => 'niiiiiiiiixiiiiiiiiiiixiiiixix',
- 'durabilityquality' => 'nf',
- 'dungeonencounter' => 'niiiisxsssxsxsxxxxxxxxx',
- 'emotes' => 'nxixxxx',
- 'emotestext' => 'nsiixxxixixxxxxxxxx',
- 'emotestextdata' => 'nsxsssxsxsxxxxxxxx',
- 'emotestextsound' => 'niiii',
- 'faction' => 'niiiiiiiiiiiiiixxxiffixsxsssxsxsxxxxxxxxxxxxxxxxxxxxxxxxx',
- 'factiontemplate' => 'nixiiiiiiiiiii',
- 'gemproperties' => 'nixxi',
- 'glyphproperties' => 'niii',
- 'gtchancetomeleecrit' => 'f',
- 'gtchancetomeleecritbase' => 'f',
- 'gtchancetospellcrit' => 'f',
- 'gtchancetospellcritbase' => 'f',
- 'gtcombatratings' => 'f',
- 'gtoctclasscombatratingscalar' => 'nf',
- 'gtoctregenhp' => 'f',
- 'gtregenmpperspt' => 'f',
- 'gtregenhpperspt' => 'f',
- 'holidays' => 'nxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxixxxxxxxxxxiisxix',
- 'holidaydescriptions' => 'nsxsssxsxsxxxxxxxx',
- 'holidaynames' => 'nsxsssxsxsxxxxxxxx',
- 'itemdisplayinfo' => 'nssxxsxxxxxiixxxxxxxxxxxx',
- 'itemgroupsounds' => 'niixx',
- 'itemextendedcost' => 'niiiiiiiiiiiiiix',
- 'itemlimitcategory' => 'nsxsssxsxsxxxxxxxxii',
- 'itemrandomproperties' => 'nsiiiiisxsssxsxsxxxxxxxx',
- 'itemrandomsuffix' => 'nsxsssxsxsxxxxxxxxsiiiiiiiiii',
- 'itemset' => 'nsxsssxsxsxxxxxxxxxxxxxxxxxxxxxxxxxiiiiiiiiiiiiiiiiii',
- 'itemsubclass' => 'iixxxxxxxixxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',
- 'lfgdungeons' => 'nsxsssxsxsxxxxxxxxiiiiiiixiixixixxxxxxxxxxxxxxxxx',
- 'lock' => 'niiiiixxxiiiiixxxiiiiixxxxxxxxxxx',
- 'locktype' => 'nsxsssxsxsxxxxxxxxsxsssxsxsxxxxxxxxsxsssxsxsxxxxxxxxs',
- 'mailtemplate' => 'nsxsssxsxsxxxxxxxxsxsssxsxsxxxxxxxx',
- 'map' => 'nsixisxsssxsxsxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxiffxixi',
- 'mapdifficulty' => 'niixxxxxxxxxxxxxxxxxxis',
- 'material' => 'nxxii',
- 'npcsounds' => 'niiix',
- 'overridespelldata' => 'niiiixixxxxx',
- 'powerdisplay' => 'nisbbb',
- 'questfactionreward' => 'niiiiiiiiii',
- 'questsort' => 'nsxsssxsxsxxxxxxxx',
- 'questxp' => 'niiiiiiiiii',
- 'randproppoints' => 'niiiiiiiiiiiiiii',
- 'scalingstatdistribution' => 'niiiiiiiiiiiiiiiiiiiii',
- 'scalingstatvalues' => 'xniiiiiiiiiiiiiiiiiiiiii',
- 'skillline' => 'nixsxsssxsxsxxxxxxxxsxsssxsxsxxxxxxxxixxxxxxxxxxxxxxxxxx',
- 'skilllineability' => 'niiiixxixiiixx',
- 'skilllinecategory' => 'nsxsssxsxsxxxxxxxxi',
- 'skillraceclassinfo' => 'niiiiixx',
- 'soundambience' => 'nii',
- 'soundemitters' => 'nffxxxxiix',
- 'soundentries' => 'nisssssssssssxxxxxxxxxxsxixxxx',
- 'spell' => 'niiiuuuuuuuuixixixixxxxxxxxxiiixxxxiiiiiiiiiiiixxiiiiiiiiiiiiiiiiiiiiiiiiiiiifffiiiiiiiiiiiiiiiiiiiiifffiiiiiiiiiiiiiiifffiiiiiiiiiiiiixsxsssxsxsxxxxxxxxsxsssxsxsxxxxxxxxsxsssxsxsxxxxxxxxsxsssxsxsxxxxxxxxiiiiiiiiiixxfffxxxiixiixifffii',
- 'spellcasttimes' => 'nixx',
- 'spelldescriptionvariables' => 'ns',
- 'spelldifficulty' => 'xiiii',
- 'spellduration' => 'nixx',
- 'spellfocusobject' => 'nsxsssxsxsxxxxxxxx',
- 'spellicon' => 'ns',
- 'spellitemenchantment' => 'niiiiiiixxxiiisxsssxsxsxxxxxxxxxxxiiii',
- 'spellitemenchantmentcondition' => 'nbbbbbxxxxxbbbbbbbbbbiiiiiXXXXX',
- 'spellradius' => 'nfxf',
- 'spellrange' => 'nffffisxsssxsxsxxxxxxxxxxxxxxxxxxxxxxxxx',
- 'spellrunecost' => 'niiii',
- 'spellshapeshiftform' => 'nxsxsssxsxsxxxxxxxxiixxiixxiiiiiiii',
- 'spellvisual' => 'niiiiiixxxxiixiixxxxxxiiiixxxxxx',
- 'spellvisualkit' => 'nxxxxxxxxxxxxxxixxxxxxxxxxxxxxxxxxxxxx',
- 'talent' => 'niiiiiiiixxxxixxixxixii',
- 'talenttab' => 'nsxsssxsxsxxxxxxxxiiiiis',
- 'taxinodes' => 'niffxsxsssxsxsxxxxxxxxxx',
- 'taxipath' => 'niix',
- 'taxipathnode' => 'niiiffxxxxx',
- 'totemcategory' => 'nsxsssxsxsxxxxxxxxiu',
- 'vocaluisounds' => 'nxiiixx',
- 'weaponimpactsounds' => 'nixiiiiiiiiiiiiiiiiiiii',
- 'weaponswingsounds2' => 'nixi',
- 'worldmaparea' => 'niisffffxix', // 4.x - niisffffxixxxx
- 'worldmapoverlay' => 'niixxxxxsiiiixxxx', // 4.x - niixxxsiiiixxxx
- 'worldmaptransforms' => 'niffffiffi',
- 'worldstatezonesounds' => 'iiiiiiix',
- 'zoneintromusictable' => 'nxixx',
- 'zonemusic' => 'nxxxxxii'
- );
-
- private $_fields = array(
- 'achievement' => 'id,faction,map,previous,name_loc0,name_loc2,name_loc3,name_loc4,name_loc6,name_loc8,description_loc0,description_loc2,description_loc3,description_loc4,description_loc6,description_loc8,category,points,orderInGroup,flags,iconId,reward_loc0,reward_loc2,reward_loc3,reward_loc4,reward_loc6,reward_loc8,reqCriteriaCount,refAchievement',
- 'achievement_category' => 'id,parentCategory',
- 'achievement_criteria' => 'id,refAchievementId,type,value1,value2,value3,value4,value5,value6,name_loc0,name_loc2,name_loc3,name_loc4,name_loc6,name_loc8,completionFlags,groupFlags,timeLimit,order',
- 'areatable' => 'id,mapId,areaTable,flags,soundAmbience,zoneMusic,zoneIntroMusic,name_loc0,name_loc2,name_loc3,name_loc4,name_loc6,name_loc8,factionGroupMask',
- 'areatrigger' => 'id,mapId,posY,posX,orientation',
- 'battlemasterlist' => 'id,mapId,moreMapId,areaType,maxPlayers,minLevel,maxLevel',
- 'charbaseinfo' => 'raceId,classId',
- 'charstartoutfit' => 'id,raceId,classId,gender,item1,item2,item3,item4,item5,item6,item7,item8,item9,item10,item11,item12,item13,item14,item15,item16,item17,item18,item19,item20',
- 'chartitles' => 'id,male_loc0,male_loc2,male_loc3,male_loc4,male_loc6,male_loc8,female_loc0,female_loc2,female_loc3,female_loc4,female_loc6,female_loc8,bitIdx',
- 'chrclasses' => 'id,powerType,name_loc0,name_loc2,name_loc3,name_loc4,name_loc6,name_loc8,fileString,flags,expansion',
- 'chrraces' => 'id,flags,factionId,baseLanguage,fileString,side,name_loc0,name_loc2,name_loc3,name_loc4,name_loc6,name_loc8,expansion',
- 'creaturedisplayinfo' => 'id,modelId,creatureSoundId,extraInfoId,skin1,skin2,skin3,iconString,npcSoundId',
- 'creaturedisplayinfoextra' => 'id,textureString',
- 'creaturefamily' => 'id,skillLine1,petFoodMask,petTalentType,categoryEnumID,name_loc0,name_loc2,name_loc3,name_loc4,name_loc6,name_loc8,iconString',
- 'creaturemodeldata' => 'id,creatureSoundId',
- 'creaturesounddata' => 'id,exertion,exertionCritical,injury,injuryCritical,death,stun,stand,footstepTerrainId,aggro,wingFlap,wingGlide,alert,fidget,customAttack,loop,jumpStart,jumpEnd,petAttack,petOrder,petDismiss,birth,spellcast,submerge,submerged',
- 'currencytypes' => 'id,itemId,category',
- 'dungeonmap' => 'id,mapId,floor,minY,maxY,minX,maxX,areaId',
- 'durabilitycosts' => 'id,w0,w1,w2,w3,w4,w5,w6,w7,w8,w10,w11,w12,w13,w14,w15,w16,w17,w18,w19,w20,a1,a2,a3,a4,a6',
- 'durabilityquality' => 'id,mod',
- 'dungeonencounter' => 'id,map,mode,order,bit,name_loc0,name_loc2,name_loc3,name_loc4,name_loc6,name_loc8',
- 'emotes' => 'id,animationId',
- 'emotestext' => 'id,command,emoteId,targetId,noTargetId,selfId',
- 'emotestextsound' => 'id,emotesTextId,raceId,gender,soundId',
- 'emotestextdata' => 'id,text_loc0,text_loc2,text_loc3,text_loc4,text_loc6,text_loc8',
- 'faction' => 'id,repIdx,baseRepRaceMask1,baseRepRaceMask2,baseRepRaceMask3,baseRepRaceMask4,baseRepClassMask1,baseRepClassMask2,baseRepClassMask3,baseRepClassMask4,baseRepValue1,baseRepValue2,baseRepValue3,baseRepValue4,repFlags1,parentFaction,spilloverRateIn,spilloverRateOut,spilloverMaxRank,name_loc0,name_loc2,name_loc3,name_loc4,name_loc6,name_loc8',
- 'factiontemplate' => 'id,factionId,ourMask,friendlyMask,hostileMask,enemyFactionId1,enemyFactionId2,enemyFactionId3,enemyFactionId4,friendFactionId1,friendFactionId2,friendFactionId3,friendFactionId4',
- 'gemproperties' => 'id,enchantmentId,colorMask',
- 'glyphproperties' => 'id,spellId,typeFlags,iconId',
- 'gtchancetomeleecrit' => 'chance',
- 'gtchancetomeleecritbase' => 'chance',
- 'gtchancetospellcrit' => 'chance',
- 'gtchancetospellcritbase' => 'chance',
- 'gtcombatratings' => 'ratio',
- 'gtoctclasscombatratingscalar' => 'idx,ratio',
- 'gtoctregenhp' => 'ratio',
- 'gtregenmpperspt' => 'ratio',
- 'gtregenhpperspt' => 'ratio',
- 'holidays' => 'id,looping,nameId,descriptionId,textureString,scheduleType',
- 'holidaydescriptions' => 'id,description_loc0,description_loc2,description_loc3,description_loc4,description_loc6,description_loc8',
- 'holidaynames' => 'id,name_loc0,name_loc2,name_loc3,name_loc4,name_loc6,name_loc8',
- 'itemdisplayinfo' => 'id,leftModelName,rightModelName,inventoryIcon1,spellVisualId,groupSoundId',
- 'itemgroupsounds' => 'id,pickUpSoundId,dropDownSoundId',
- 'itemextendedcost' => 'id,reqHonorPoints,reqArenaPoints,reqArenaSlot,reqItemId1,reqItemId2,reqItemId3,reqItemId4,reqItemId5,itemCount1,itemCount2,itemCount3,itemCount4,itemCount5,reqPersonalRating',
- 'itemlimitcategory' => 'id,name_loc0,name_loc2,name_loc3,name_loc4,name_loc6,name_loc8,count,isGem',
- 'itemrandomproperties' => 'id,nameINT,enchantId1,enchantId2,enchantId3,enchantId4,enchantId5,name_loc0,name_loc2,name_loc3,name_loc4,name_loc6,name_loc8',
- 'itemrandomsuffix' => 'id,name_loc0,name_loc2,name_loc3,name_loc4,name_loc6,name_loc8,nameINT,enchantId1,enchantId2,enchantId3,enchantId4,enchantId5,allocationPct1,allocationPct2,allocationPct3,allocationPct4,allocationPct5',
- 'itemset' => 'id,name_loc0,name_loc2,name_loc3,name_loc4,name_loc6,name_loc8,spellId1,spellId2,spellId3,spellId4,spellId5,spellId6,spellId7,spellId8,itemCount1,itemCount2,itemCount3,itemCount4,itemCount5,itemCount6,itemCount7,itemCount8,reqSkillId,reqSkillLevel',
- 'itemsubclass' => 'class,subClass,weaponSize',
- 'lfgdungeons' => 'id,name_loc0,name_loc2,name_loc3,name_loc4,name_loc6,name_loc8,levelMin,levelMax,targetLevel,targetLevelMin,targetLevelMax,mapId,difficulty,type,faction,expansion,groupId',
- 'lock' => 'id,type1,type2,type3,type4,type5,properties1,properties2,properties3,properties4,properties5,reqSkill1,reqSkill2,reqSkill3,reqSkill4,reqSkill5',
- 'locktype' => 'id,name_loc0,name_loc2,name_loc3,name_loc4,name_loc6,name_loc8,state_loc0,state_loc2,state_loc3,state_loc4,state_loc6,state_loc8,process_loc0,process_loc2,process_loc3,process_loc4,process_loc6,process_loc8,strref',
- 'mailtemplate' => 'id,subject_loc0,subject_loc2,subject_loc3,subject_loc4,subject_loc6,subject_loc8,text_loc0,text_loc2,text_loc3,text_loc4,text_loc6,text_loc8',
- 'map' => 'id,nameINT,areaType,isBG,name_loc0,name_loc2,name_loc3,name_loc4,name_loc6,name_loc8,parentMapId,parentX,parentY,expansion,maxPlayers',
- 'mapdifficulty' => 'id,mapId,difficulty,nPlayer,nPlayerString',
- 'material' => 'id,sheatheSoundId,unsheatheSoundId',
- 'npcsounds' => 'id,greetSoundId,byeSoundId,angrySoundId',
- 'overridespelldata' => 'id,spellId1,spellId2,spellId3,spellId4,spellId5',
- 'powerdisplay' => 'id,realType,globalString,r,g,b',
- 'questfactionreward' => 'id,field1,field2,field3,field4,field5,field6,field7,field8,field9,field10',
- 'questsort' => 'id,name_loc0,name_loc2,name_loc3,name_loc4,name_loc6,name_loc8',
- 'questxp' => 'id,field1,field2,field3,field4,field5,field6,field7,field8,field9,field10',
- 'randproppoints' => 'id,epic1,epic2,epic3,epic4,epic5,rare1,rare2,rare3,rare4,rare5,uncommon1,uncommon2,uncommon3,uncommon4,uncommon5',
- 'scalingstatdistribution' => 'id,statMod1,statMod2,statMod3,statMod4,statMod5,statMod6,statMod7,statMod8,statMod9,statMod10,modifier1,modifier2,modifier3,modifier4,modifier5,modifier6,modifier7,modifier8,modifier9,modifier10,maxLevel',
- 'scalingstatvalues' => 'id,shoulderMultiplier,trinketMultiplier,weaponMultiplier,rangedMultiplier,clothShoulderArmor,leatherShoulderArmor,mailShoulderArmor,plateShoulderArmor,weaponDPS1H,weaponDPS2H,casterDPS1H,casterDPS2H,rangedDPS,wandDPS,spellPower,primBudged,tertBudged,clothCloakArmor,clothChestArmor,leatherChestArmor,mailChestArmor,plateChestArmor',
- 'skillline' => 'id,categoryId,name_loc0,name_loc2,name_loc3,name_loc4,name_loc6,name_loc8,description_loc0,description_loc2,description_loc3,description_loc4,description_loc6,description_loc8,iconId',
- 'skilllineability' => 'id,skillLineId,spellId,reqRaceMask,reqClassMask,reqSkillLevel,acquireMethod,skillLevelGrey,skillLevelYellow',
- 'skilllinecategory' => 'id,name_loc0,name_loc2,name_loc3,name_loc4,name_loc6,name_loc8,index',
- 'skillraceclassinfo' => 'id,skillLine,raceMask,classMask,flags,reqLevel',
- 'soundambience' => 'id,soundIdDay,soundIdNight',
- 'soundemitters' => 'id,posY,posX,soundId,mapId',
- 'soundentries' => 'id,type,name,file1,file2,file3,file4,file5,file6,file7,file8,file9,file10,path,flags',
- 'spell' => 'id,category,dispelType,mechanic,attributes0,attributes1,attributes2,attributes3,attributes4,attributes5,attributes6,attributes7,stanceMask,stanceMaskNot,targets,spellFocus,castTimeId,recoveryTime,recoveryTimeCategory,procChance,procCharges,maxLevel,baseLevel,spellLevel,durationId,powerType,powerCost,powerCostPerLevel,powerPerSecond,powerPerSecondPerLevel,rangeId,stackAmount,tool1,tool2,reagent1,reagent2,reagent3,reagent4,reagent5,reagent6,reagent7,reagent8,reagentCount1,reagentCount2,reagentCount3,reagentCount4,reagentCount5,reagentCount6,reagentCount7,reagentCount8,equippedItemClass,equippedItemSubClassMask,equippedItemInventoryTypeMask,effect1Id,effect2Id,effect3Id,effect1DieSides,effect2DieSides,effect3DieSides,effect1RealPointsPerLevel,effect2RealPointsPerLevel,effect3RealPointsPerLevel,effect1BasePoints,effect2BasePoints,effect3BasePoints,effect1Mechanic,effect2Mechanic,effect3Mechanic,effect1ImplicitTargetA,effect2ImplicitTargetA,effect3ImplicitTargetA,effect1ImplicitTargetB,effect2ImplicitTargetB,effect3ImplicitTargetB,effect1RadiusId,effect2RadiusId,effect3RadiusId,effect1AuraId,effect2AuraId,effect3AuraId,effect1Periode,effect2Periode,effect3Periode,effect1ValueMultiplier,effect2ValueMultiplier,effect3ValueMultiplier,effect1ChainTarget,effect2ChainTarget,effect3ChainTarget,effect1CreateItemId,effect2CreateItemId,effect3CreateItemId,effect1MiscValue,effect2MiscValue,effect3MiscValue,effect1MiscValueB,effect2MiscValueB,effect3MiscValueB,effect1TriggerSpell,effect2TriggerSpell,effect3TriggerSpell,effect1PointsPerComboPoint,effect2PointsPerComboPoint,effect3PointsPerComboPoint,effect1SpellClassMaskA,effect2SpellClassMaskA,effect3SpellClassMaskA,effect1SpellClassMaskB,effect2SpellClassMaskB,effect3SpellClassMaskB,effect1SpellClassMaskC,effect2SpellClassMaskC,effect3SpellClassMaskC,spellVisualId1,spellVisualId2,iconId,iconIdActive,name_loc0,name_loc2,name_loc3,name_loc4,name_loc6,name_loc8,rank_loc0,rank_loc2,rank_loc3,rank_loc4,rank_loc6,rank_loc8,description_loc0,description_loc2,description_loc3,description_loc4,description_loc6,description_loc8,buff_loc0,buff_loc2,buff_loc3,buff_loc4,buff_loc6,buff_loc8,powerCostPercent,startRecoveryCategory,startRecoveryTime,maxTargetLevel,spellFamilyId,spellFamilyFlags1,spellFamilyFlags2,spellFamilyFlags3,maxAffectedTargets,damageClass,effect1DamageMultiplier,effect2DamageMultiplier,effect3DamageMultiplier,toolCategory1,toolCategory2,schoolMask,runeCostId,powerDisplayId,effect1BonusMultiplier,effect2BonusMultiplier,effect3BonusMultiplier,spellDescriptionVariable,spellDifficulty',
- 'spellcasttimes' => 'id,baseTime',
- 'spelldescriptionvariables' => 'id,vars',
- 'spellduration' => 'id,baseTime',
- 'spelldifficulty' => 'normal10,normal25,heroic10,heroic25',
- 'spellfocusobject' => 'id,name_loc0,name_loc2,name_loc3,name_loc4,name_loc6,name_loc8',
- 'spellicon' => 'id,iconPath',
- 'spellitemenchantment' => 'id,charges,type1,type2,type3,amount1,amount2,amount3,object1,object2,object3,name_loc0,name_loc2,name_loc3,name_loc4,name_loc6,name_loc8,conditionId,skillLine,skillLevel,requiredLevel',
- 'spellitemenchantmentcondition' => 'id,color1,color2,color3,color4,color5,comparator1,comparator2,comparator3,comparator4,comparator5,cmpColor1,cmpColor2,cmpColor3,cmpColor4,cmpColor5,value1,value2,value3,value4,value5',
- 'spellradius' => 'id,radiusMin,radiusMax',
- 'spellrange' => 'id,rangeMinHostile,rangeMinFriend,rangeMaxHostile,rangeMaxFriend,rangeType,name_loc0,name_loc2,name_loc3,name_loc4,name_loc6,name_loc8',
- 'spellrunecost' => 'id,costBlood,costUnholy,costFrost,runicPowerGain',
- 'spellshapeshiftform' => 'id,name_loc0,name_loc2,name_loc3,name_loc4,name_loc6,name_loc8,flags,creatureType,displayIdA,displayIdH,spellId1,spellId2,spellId3,spellId4,spellId5,spellId6,spellId7,spellId8',
- 'spellvisual' => 'id,precastKitId,castKitId,impactKitId,stateKitId,statedoneKitId,channelKitId,missileSoundId,animationSoundId,casterImpactKitId,targetImpactKitId,missileTargetingKitId,instantAreaKitId,impactAreaKitId,persistentAreaKitId',
- 'spellvisualkit' => 'id,soundId',
- 'talent' => 'id,tabId,row,column,rank1,rank2,rank3,rank4,rank5,reqTalent,reqRank,talentSpell,petCategory1,petCategory2',
- 'talenttab' => 'id,name_loc0,name_loc2,name_loc3,name_loc4,name_loc6,name_loc8,iconId,raceMask,classMask,creatureFamilyMask,tabNumber,textureFile',
- 'taxinodes' => 'id,mapId,posX,posY,name_loc0,name_loc2,name_loc3,name_loc4,name_loc6,name_loc8',
- 'taxipath' => 'id,startNodeId,endNodeId',
- 'taxipathnode' => 'id,pathId,nodeIdx,mapId,posX,posY',
- 'totemcategory' => 'id,name_loc0,name_loc2,name_loc3,name_loc4,name_loc6,name_loc8,category,categoryMask',
- 'vocaluisounds' => 'id,raceId,soundIdMale,soundIdFemale',
- 'weaponimpactsounds' => 'id,subClass,hit1,hit2,hit3,hit4,hit5,hit6,hit7,hit8,hit9,hit10,crit1,crit2,crit3,crit4,crit5,crit6,crit7,crit8,crit9,crit10',
- 'weaponswingsounds2' => 'id,weaponSize,soundId',
- 'worldmaparea' => 'id,mapId,areaId,nameINT,left,right,top,bottom,defaultDungeonMapId',
- 'worldmapoverlay' => 'id,worldMapAreaId,areaTableId,textureString,w,h,x,y',
- 'worldmaptransforms' => 'id,sourceMapId,minX,minY,maxX,maxY,targetMapId,offsetX,offsetY,dungeonMapId',
- 'worldstatezonesounds' => 'stateId,value,areaId,wmoAreaId,zoneIntroMusicId,zoneMusicId,soundAmbienceId',
- 'zoneintromusictable' => 'id,soundId',
- 'zonemusic' => 'id,soundIdDay,soundIdNight'
- );
-
- private $isGameTable = false;
- private $localized = false;
- private $tempTable = true;
- private $tableName = '';
-
- private $dataBuffer = [];
- private $bufferSize = 500;
-
- private $fileRefs = [];
-
- public $error = true;
- public $fields = [];
- public $format = '';
- public $file = '';
-
-
- public function __construct($file, $opts = [])
- {
- $file = strtolower($file);
- if (empty($this->_fields[$file]) || empty($this->_formats[$file]))
- {
- CLI::write('no structure known for '.$file.'.dbc, aborting.', CLI::LOG_ERROR);
- return;
- }
-
- $this->fields = explode(',', $this->_fields[$file]);
- $this->format = $this->_formats[$file];
- $this->file = $file;
- $this->localized = !!strstr($this->format, 'sxsssxsxsxxxxxxxx');
-
- if (count($this->fields) != strlen(str_ireplace('x', '', $this->format)))
- {
- CLI::write('known field types ['.count($this->fields).'] and names ['.strlen(str_ireplace('x', '', $this->format)).'] do not match for '.$file.'.dbc, aborting.', CLI::LOG_ERROR);
- return;
- }
-
- if (is_bool($opts['temporary']))
- $this->tempTable = $opts['temporary'];
-
- if (!empty($opts['tableName']))
- $this->tableName = $opts['tableName'];
- else
- $this->tableName = 'dbc_'.$file;
-
- // gameTable-DBCs don't have an index and are accessed through value order
- // allas, you cannot do this with mysql, so we add a 'virtual' index
- $this->isGameTable = $this->format == 'f' && substr($file, 0, 2) == 'gt';
-
- $foundMask = 0x0;
- foreach (CLISetup::$expectedPaths as $locStr => $locId)
- {
- if (!in_array($locId, CLISetup::$localeIds))
- continue;
-
- if ($foundMask & (1 << $locId))
- continue;
-
- $fullPath = CLI::nicePath($this->file.'.dbc', CLISetup::$srcDir, $locStr, 'DBFilesClient');
- if (!CLISetup::fileExists($fullPath))
- continue;
-
- $this->curFile = $fullPath;
- if ($this->validateFile($locId))
- $foundMask |= (1 << $locId);
- }
-
- if (!$this->fileRefs)
- {
- CLI::write('no suitable files found for '.$file.'.dbc, aborting.', CLI::LOG_ERROR);
- return;
- }
-
- // check if DBCs are identical
- $headers = array_column($this->fileRefs, 2);
- $x = array_unique(array_column($headers, 'recordCount'));
- if (count($x) != 1)
- {
- CLI::write('some DBCs have differenct record counts ('.implode(', ', $x).' respectively). cannot merge!', CLI::LOG_ERROR);
- return;
- }
- $x = array_unique(array_column($headers, 'fieldCount'));
- if (count($x) != 1)
- {
- CLI::write('some DBCs have differenct field counts ('.implode(', ', $x).' respectively). cannot merge!', CLI::LOG_ERROR);
- return;
- }
- $x = array_unique(array_column($headers, 'recordSize'));
- if (count($x) != 1)
- {
- CLI::write('some DBCs have differenct record sizes ('.implode(', ', $x).' respectively). cannot merge!', CLI::LOG_ERROR);
- return;
- }
-
- $this->error = false;
- }
-
- public function readFile()
- {
- if (!$this->file || $this->error)
- return [];
-
- $this->createTable();
-
- if ($this->localized)
- CLI::write(' - reading and merging '.$this->file.'.dbc for locales '.implode(', ', array_keys($this->fileRefs)));
- else
- CLI::write(' - reading '.$this->file.'.dbc');
-
- if (!$this->read())
- {
- CLI::write(' - DBC::read() returned with error', CLI::LOG_ERROR);
- return false;
- }
-
- return true;
- }
-
- private function endClean()
- {
- foreach ($this->fileRefs as &$ref)
- fclose($ref[0]);
-
- $this->dataBuffer = null;
- }
-
- private function readHeader(&$handle = null)
- {
- if (!is_resource($handle))
- $handle = fopen($this->curFile, 'rb');
-
- if (!$handle)
- return false;
-
- if (fread($handle, 4) != 'WDBC')
- {
- CLI::write('file '.$this->curFile.' has incorrect magic bytes', CLI::LOG_ERROR);
- fclose($handle);
- return false;
- }
-
- return unpack('VrecordCount/VfieldCount/VrecordSize/VstringSize', fread($handle, 16));
- }
-
- private function validateFile($locId)
- {
- $filesize = filesize($this->curFile);
- if ($filesize < 20)
- {
- CLI::write('file '.$this->curFile.' is too small for a DBC file', CLI::LOG_ERROR);
- return false;
- }
-
- $header = $this->readHeader($handle);
- if (!$header)
- {
- CLI::write('cannot open file '.$this->curFile, CLI::LOG_ERROR);
- return false;
- }
-
- // Different debug checks to be sure, that file was opened correctly
- $debugStr = '(recordCount='.$header['recordCount'].
- ' fieldCount=' .$header['fieldCount'] .
- ' recordSize=' .$header['recordSize'] .
- ' stringSize=' .$header['stringSize'] .')';
-
- if ($header['recordCount'] * $header['recordSize'] + $header['stringSize'] + 20 != $filesize)
- {
- CLI::write('file '.$this->curFile.' has incorrect size '.$filesize.': '.$debugStr, CLI::LOG_ERROR);
- fclose($handle);
- return false;
- }
-
- if ($header['fieldCount'] != strlen($this->format))
- {
- CLI::write('incorrect format string ('.$this->format.') specified for file '.$this->curFile.' fieldCount='.$header['fieldCount'], CLI::LOG_ERROR);
- fclose($handle);
- return false;
- }
-
- $this->fileRefs[$locId] = [$handle, $this->curFile, $header];
-
- return true;
- }
-
- private function createTable()
- {
- if ($this->error)
- return;
-
- $n = 0;
- $pKey = '';
- $query = 'CREATE '.($this->tempTable ? 'TEMPORARY' : '').' TABLE `'.$this->tableName.'` (';
-
- if ($this->isGameTable)
- {
- $query .= '`idx` BIGINT(20) NOT NULL, ';
- $pKey = 'idx';
- }
-
- foreach (str_split($this->format) as $idx => $f)
- {
- switch ($f)
- {
- case 'f':
- $query .= '`'.$this->fields[$n].'` FLOAT NOT NULL, ';
- break;
- case 's':
- $query .= '`'.$this->fields[$n].'` TEXT NOT NULL, ';
- break;
- case 'i':
- case 'n':
- case 'b':
- case 'u':
- $query .= '`'.$this->fields[$n].'` BIGINT(20) NOT NULL, ';
- break;
- default: // 'x', 'X', 'd'
- continue 2;
- }
-
- if ($f == 'n')
- $pKey = $this->fields[$n];
-
- $n++;
- }
-
- if ($pKey)
- $query .= 'PRIMARY KEY (`'.$pKey.'`) ';
- else
- $query = substr($query, 0, -2);
-
- $query .= ') COLLATE=\'utf8_general_ci\' ENGINE=MyISAM';
-
- DB::Aowow()->query('DROP TABLE IF EXISTS ?#', $this->tableName);
- DB::Aowow()->query($query);
- }
-
- private function writeToDB()
- {
- if (!$this->dataBuffer || $this->error)
- return;
-
- // make inserts more manageable
- $fields = $this->fields;
-
- if ($this->isGameTable)
- array_unshift($fields, 'idx');
-
- DB::Aowow()->query('INSERT INTO ?# (?#) VALUES (?a)', $this->tableName, $fields, $this->dataBuffer);
- $this->dataBuffer = [];
- }
-
- private function read()
- {
- // l - signed long (always 32 bit, machine byte order)
- // V - unsigned long (always 32 bit, little endian byte order)
- $unpackStr = '';
- $unpackFmt = array(
- 'x' => 'x/x/x/x',
- 'X' => 'x',
- 's' => 'V',
- 'f' => 'f',
- 'i' => 'l', // not sure if 'l' or 'V' should be used here
- 'u' => 'V',
- 'b' => 'C',
- 'd' => 'x4',
- 'n' => 'V'
- );
-
- // Check that record size also matches
- $recSize = 0;
- for ($i = 0; $i < strlen($this->format); $i++)
- {
- $ch = $this->format[$i];
- if ($ch == 'X' || $ch == 'b')
- $recSize += 1;
- else
- $recSize += 4;
-
- if (!isset($unpackFmt[$ch]))
- {
- CLI::write('unknown format parameter \''.$ch.'\' in format string', CLI::LOG_ERROR);
- return false;
- }
-
- $unpackStr .= '/'.$unpackFmt[$ch];
-
- if ($ch != 'X' && $ch != 'x')
- $unpackStr .= 'f'.$i;
- }
-
- $unpackStr = substr($unpackStr, 1);
-
- // Optimizing unpack string: 'x/x/x/x/x/x' => 'x6'
- while (preg_match('/(x\/)+x/', $unpackStr, $r))
- $unpackStr = substr_replace($unpackStr, 'x'.((strlen($r[0]) + 1) / 2), strpos($unpackStr, $r[0]), strlen($r[0]));
-
-
- // we asserted all DBCs to be identical in structure. pick first header for checks
- $header = reset($this->fileRefs)[2];
-
- if ($recSize != $header['recordSize'])
- {
- CLI::write('format string size ('.$recSize.') for file '.$this->file.' does not match actual size ('.$header['recordSize'].')', CLI::LOG_ERROR);
- return false;
- }
-
- // And, finally, extract the records
- $strings = [];
- $rSize = $header['recordSize'];
- $rCount = $header['recordCount'];
- $fCount = strlen($this->format);
- $strBlock = 4 + 16 + $header['recordSize'] * $header['recordCount'];
-
- for ($i = 0; $i < $rCount; $i++)
- {
- $row = [];
- $idx = $i;
-
- // add 'virtual' enumerator for gt*-dbcs
- if ($this->isGameTable)
- $row[-1] = $i;
-
- foreach ($this->fileRefs as $locId => [$handle, $fullPath, $header])
- {
- $rec = unpack($unpackStr, fread($handle, $header['recordSize']));
-
- $n = -1;
- for ($j = 0; $j < $fCount; $j++)
- {
- if (!isset($rec['f'.$j]))
- continue;
-
- if (!empty($row[$j]))
- continue;
-
- $n++;
-
- switch ($this->format[$j])
- {
- case 's':
- $curPos = ftell($handle);
- fseek($handle, $strBlock + $rec['f'.$j]);
-
- $str = $chr = '';
- do
- {
- $str .= $chr;
- $chr = fread($handle, 1);
- }
- while ($chr != "\000");
-
- fseek($handle, $curPos);
- $row[$j] = $str;
- break;
- case 'f':
- $row[$j] = round($rec['f'.$j], 8);
- break;
- case 'n': // DO NOT BREAK!
- $idx = $rec['f'.$j];
- default: // nothing special .. 'i', 'u' and the likes
- $row[$j] = $rec['f'.$j];
- }
- }
-
- if (!$this->localized) // one match is enough
- break;
- }
-
- $this->dataBuffer[$idx] = array_values($row);
-
- if (count($this->dataBuffer) >= $this->bufferSize)
- $this->writeToDB();
- }
-
- $this->writeToDB();
-
- $this->endCLean();
-
- return true;
- }
-}
-
-?>
diff --git a/setup/tools/dbc/12340.ini b/setup/tools/dbc/12340.ini
new file mode 100644
index 00000000..808fa661
--- /dev/null
+++ b/setup/tools/dbc/12340.ini
@@ -0,0 +1,1538 @@
+; DBC structure - 3.3.5.12340
+;
+; x - not used/unknown, 4 bytes
+; X - not used/unknown, 1 byte
+; s - string block index, 4 bytes
+; S - string block index, 4 bytes - localized; autofill; not used in 3.3.5
+; f - float, 4 bytes (rounded to 4 digits after comma)
+; u - unsigned int, 4 bytes
+; i - signed int, 4 bytes
+; b - unsigned char, 1 byte
+; d - sorted by this field, not included in array
+; n - same, but field included in array
+;
+; LOC - used locale strings macro [sxsssxsxsxxxxxxxx]
+; X_LOC - unused locale strings macro [xxxxxxxxxxxxxxxxx]
+
+[achievement]
+id = n
+faction = i
+map = i
+previous = i
+name = LOC
+description = LOC
+category = i
+points = i
+orderInGroup = i
+flags = i
+iconId = i
+reward = LOC
+reqCriteriaCount = i
+refAchievement = i
+
+[achievement_category]
+id = n
+parentCategory = i
+UNUSED2 = X_LOC
+UNUSED3 = x
+
+[achievement_criteria]
+id = n
+refAchievementId = i
+type = I
+value1 = I
+value2 = i
+value3 = i
+value4 = i
+value5 = i
+value6 = i
+name = LOC
+completionFlags = i
+groupFlags = i
+UNUSED12 = x
+timeLimit = i
+order = i
+
+[areatable]
+id = n
+mapId = i
+areaTable = i
+areaBit = x
+flags = i
+soundProviderPref = x
+soundProviderPrefWater = x
+soundAmbience = i
+zoneMusic = i
+zoneIntroMusic = i
+explorationLevel = x
+name = LOC
+factionGroupMask = i
+liquidType1 = x
+liquidType2 = x
+liquidType3 = x
+liquidType4 = x
+minElevation = x
+ambientMultiplier = x
+lightId = x
+
+[areatrigger]
+id = n
+mapId = i
+posX = f
+posY = f
+UNUSED4 = x
+UNUSED5 = x
+UNUSED6 = x
+UNUSED7 = x
+UNUSED8 = x
+orientation = f
+
+[battlemasterlist]
+id = n
+mapId = i
+moreMapId = i
+UNUSED3 = x
+UNUSED4 = x
+UNUSED5 = x
+UNUSED6 = x
+UNUSED7 = x
+UNUSED8 = x
+areaType = i
+UNUSED10 = X_LOC
+UNUSED11 = x
+maxPlayers = i
+UNUSED13 = x
+minLevel = i
+maxLevel = i
+
+[charbaseinfo]
+raceId = b
+classId = b
+
+[charstartoutfit]
+id = n
+raceId = b
+classId = b
+gender = b
+UNUSED4 = X
+item1 = i
+item2 = i
+item3 = i
+item4 = i
+item5 = i
+item6 = i
+item7 = i
+item8 = i
+item9 = i
+item10 = i
+item11 = i
+item12 = i
+item13 = i
+item14 = i
+item15 = i
+item16 = i
+item17 = i
+item18 = i
+item19 = i
+item20 = i
+UNUSED25 = X_LOC
+UNUSED26 = X_LOC
+UNUSED27 = X_LOC
+UNUSED28 = x
+
+[chartitles]
+id = n
+UNUSED1 = x
+male = LOC
+female = LOC
+bitIdx = i
+
+[chrclasses]
+id = n
+UNUSED1 = x
+powerType = i
+UNUSED3 = x
+name = LOC
+UNUSED5 = X_LOC
+UNUSED6 = X_LOC
+fileString = s
+UNUSED8 = x
+flags = i
+UNUSED10 = x
+expansion = i
+
+[chrraces]
+id = n
+flags = i
+factionId = i
+UNUSED3 = x
+UNUSED4 = x
+UNUSED5 = x
+UNUSED6 = x
+baseLanguage = i
+UNUSED8 = x
+UNUSED9 = x
+UNUSED10 = x
+fileString = s
+UNUSED12 = x
+side = i
+name = LOC
+UNUSED15 = X_LOC
+UNUSED16 = X_LOC
+UNUSED17 = x
+UNUSED18 = x
+UNUSED19 = x
+expansion = i
+
+[creaturedisplayinfo]
+id = n
+modelId = i
+creatureSoundId = i
+extraInfoId = i
+UNUSED4 = x
+UNUSED5 = x
+skin1 = s
+skin2 = s
+skin3 = s
+iconString = s
+UNUSED10 = x
+UNUSED11 = x
+npcSoundId = i
+UNUSED13 = x
+UNUSED14 = x
+UNUSED15 = x
+
+[creaturedisplayinfoextra]
+id = n
+UNUSED1 = X_LOC
+UNUSED2 = x
+UNUSED3 = x
+textureString = s
+
+[creaturefamily]
+id = n
+UNUSED1 = x
+UNUSED2 = x
+UNUSED3 = x
+UNUSED4 = x
+skillLine1 = i
+UNUSED6 = x
+petFoodMask = i
+petTalentType = i
+categoryEnumID = i
+name = LOC
+iconString = s
+
+[creaturemodeldata]
+id = n
+UNUSED1 = x
+UNUSED2 = x
+UNUSED3 = x
+UNUSED4 = x
+UNUSED5 = x
+UNUSED6 = x
+UNUSED7 = x
+UNUSED8 = x
+UNUSED9 = x
+UNUSED10 = x
+UNUSED11 = x
+UNUSED12 = x
+creatureSoundId = i
+UNUSED14 = x
+UNUSED15 = x
+UNUSED16 = x
+UNUSED17 = x
+UNUSED18 = x
+UNUSED19 = x
+UNUSED20 = x
+UNUSED21 = x
+UNUSED22 = x
+UNUSED23 = x
+UNUSED24 = x
+UNUSED25 = x
+UNUSED26 = x
+UNUSED27 = x
+
+[creaturesounddata]
+id = n
+exertion = i
+exertionCritical = i
+injury = i
+injuryCritical = i
+UNUSED5 = x
+death = i
+stun = i
+stand = i
+footstepTerrainId = i
+aggro = i
+wingFlap = i
+wingGlide = i
+alert = i
+fidget = i
+UNUSED15 = x
+UNUSED16 = x
+UNUSED17 = x
+UNUSED18 = x
+customAttack = i
+UNUSED20 = x
+UNUSED21 = x
+UNUSED22 = x
+UNUSED23 = x
+loop = i
+UNUSED25 = x
+jumpStart = i
+jumpEnd = i
+petAttack = i
+petOrder = i
+petDismiss = i
+UNUSED31 = x
+UNUSED32 = x
+birth = i
+spellcast = i
+submerge = i
+submerged = i
+UNUSED37 = x
+
+[currencytypes]
+id = n
+itemId = i
+category = i
+UNUSED3 = x
+
+[declinedword]
+id = n
+word = s
+
+[declinedwordcases]
+id = n
+wordId = i
+caseIdx = i
+word = s
+
+[dungeonmap]
+id = n
+mapId = i
+floor = i
+minY = f
+maxY = f
+minX = f
+maxX = f
+worldMapAreaId = i
+
+[durabilitycosts]
+id = n
+w0 = i
+w1 = i
+w2 = i
+w3 = i
+w4 = i
+w5 = i
+w6 = i
+w7 = i
+w8 = i
+UNUSED10 = x
+w10 = i
+w11 = i
+w12 = i
+w13 = i
+w14 = i
+w15 = i
+w16 = i
+w17 = i
+w18 = i
+w19 = i
+w20 = i
+UNUSED22 = x
+a1 = i
+a2 = i
+a3 = i
+a4 = i
+UNUSED27 = x
+a6 = i
+UNUSED29 = x
+
+[durabilityquality]
+id = n
+mod = f
+
+[dungeonencounter]
+id = n
+map = i
+mode = i
+order = i
+bit = i
+name = LOC
+UNUSED6 = x
+
+[emotes]
+id = n
+name = s
+animationId = i
+flags = i
+state = i
+stateParam = i
+soundId = i
+
+[emotestext]
+id = n
+command = s
+emoteId = i
+etd0 = i
+etd1 = i
+etd2 = i
+UNUSED6 = x
+etd4 = i
+UNUSED8 = x
+etd6 = i
+UNUSED10 = x
+etd8 = i
+etd9 = i
+UNUSED13 = x
+UNUSED14 = x
+etd12 = i
+UNUSED16 = x
+UNUSED17 = x
+UNUSED18 = x
+
+[emotestextsound]
+id = n
+emotesTextId = i
+raceId = i
+gender = i
+soundId = i
+
+[emotestextdata]
+id = n
+text = LOC
+
+[faction]
+id = n
+repIdx = i
+baseRepRaceMask1 = i
+baseRepRaceMask2 = i
+baseRepRaceMask3 = i
+baseRepRaceMask4 = i
+baseRepClassMask1 = i
+baseRepClassMask2 = i
+baseRepClassMask3 = i
+baseRepClassMask4 = i
+baseRepValue1 = i
+baseRepValue2 = i
+baseRepValue3 = i
+baseRepValue4 = i
+repFlags1 = i
+UNUSED15 = x
+UNUSED16 = x
+UNUSED17 = x
+parentFaction = i
+spilloverRateIn = f
+spilloverRateOut = f
+spilloverMaxRank = i
+UNUSED22 = x
+name = LOC
+UNUSED24 = X_LOC
+
+[factiontemplate]
+id = n
+factionId = i
+UNUSED2 = x
+ourMask = i
+friendlyMask = i
+hostileMask = i
+enemyFactionId1 = i
+enemyFactionId2 = i
+enemyFactionId3 = i
+enemyFactionId4 = i
+friendFactionId1 = i
+friendFactionId2 = i
+friendFactionId3 = i
+friendFactionId4 = i
+
+[gemproperties]
+id = n
+enchantmentId = i
+UNUSED2 = x
+UNUSED3 = x
+colorMask = i
+
+[glyphproperties]
+id = n
+spellId = i
+typeFlags = i
+iconId = i
+
+[gtchancetomeleecrit]
+chance = f
+
+[gtchancetomeleecritbase]
+chance = f
+
+[gtchancetospellcrit]
+chance = f
+
+[gtchancetospellcritbase]
+chance = f
+
+[gtcombatratings]
+ratio = f
+
+[gtnpcmanacostscaler]
+factor = f
+
+[gtoctclasscombatratingscalar]
+idx = n
+ratio = f
+
+[gtoctregenhp]
+ratio = f
+
+[gtregenmpperspt]
+ratio = f
+
+[gtregenhpperspt]
+ratio = f
+
+[holidays]
+id = n
+UNUSED1 = X_LOC
+UNUSED2 = X_LOC
+UNUSED3 = x
+UNUSED4 = x
+UNUSED5 = x
+looping = i
+UNUSED7 = x
+UNUSED8 = x
+UNUSED9 = x
+UNUSED10 = x
+UNUSED11 = x
+UNUSED12 = x
+UNUSED13 = x
+UNUSED14 = x
+UNUSED15 = x
+UNUSED16 = x
+nameId = i
+descriptionId = i
+textureString = s
+UNUSED20 = x
+scheduleType = i
+UNUSED22 = x
+
+[holidaydescriptions]
+id = n
+description = LOC
+
+[holidaynames]
+id = n
+name = LOC
+
+[item]
+id = n
+classId = i
+subClassId = i
+soundOverride = i
+material = i
+displayInfoId = i
+inventoryType = i
+sheatheType = i
+
+[itemdisplayinfo]
+id = n
+leftModelName = s
+rightModelName = s
+UNUSED3 = x
+UNUSED4 = x
+inventoryIcon1 = s
+UNUSED6 = x
+UNUSED7 = x
+UNUSED8 = x
+UNUSED9 = x
+UNUSED10 = x
+spellVisualId = i
+groupSoundId = i
+UNUSED13 = x
+UNUSED14 = x
+UNUSED15 = x
+UNUSED16 = x
+UNUSED17 = x
+UNUSED18 = x
+UNUSED19 = x
+UNUSED20 = x
+UNUSED21 = x
+UNUSED22 = x
+UNUSED23 = x
+UNUSED24 = x
+
+[itemgroupsounds]
+id = n
+pickUpSoundId = i
+dropDownSoundId = i
+UNUSED3 = x
+UNUSED4 = x
+
+[itemextendedcost]
+id = n
+reqHonorPoints = i
+reqArenaPoints = i
+reqArenaSlot = i
+reqItemId1 = i
+reqItemId2 = i
+reqItemId3 = i
+reqItemId4 = i
+reqItemId5 = i
+itemCount1 = i
+itemCount2 = i
+itemCount3 = i
+itemCount4 = i
+itemCount5 = i
+reqPersonalRating = i
+UNUSED15 = x
+
+[itemlimitcategory]
+id = n
+name = LOC
+count = i
+isGem = i
+
+[itemrandomproperties]
+id = n
+nameINT = s
+enchantId1 = i
+enchantId2 = i
+enchantId3 = i
+enchantId4 = i
+enchantId5 = i
+name = LOC
+
+[itemrandomsuffix]
+id = n
+name = LOC
+nameINT = s
+enchantId1 = i
+enchantId2 = i
+enchantId3 = i
+enchantId4 = i
+enchantId5 = i
+alLOCationPct1 = i
+alLOCationPct2 = i
+alLOCationPct3 = i
+alLOCationPct4 = i
+alLOCationPct5 = i
+
+[itemset]
+id = n
+name = LOC
+UNUSED2 = X_LOC
+spellId1 = i
+spellId2 = i
+spellId3 = i
+spellId4 = i
+spellId5 = i
+spellId6 = i
+spellId7 = i
+spellId8 = i
+itemCount1 = i
+itemCount2 = i
+itemCount3 = i
+itemCount4 = i
+itemCount5 = i
+itemCount6 = i
+itemCount7 = i
+itemCount8 = i
+reqSkillId = i
+reqSkillLevel = i
+
+[itemsubclass]
+class = i
+subClass = i
+UNUSED2 = x
+UNUSED3 = x
+UNUSED4 = x
+UNUSED5 = x
+UNUSED6 = x
+UNUSED7 = x
+UNUSED8 = x
+weaponSize = i
+UNUSED10 = X_LOC
+UNUSED11 = X_LOC
+
+[lfgdungeons]
+id = n
+name = LOC
+levelMin = i
+levelMax = i
+targetLevel = i
+targetLevelMin = i
+targetLevelMax = i
+mapId = i
+difficulty = i
+UNUSED9 = x
+type = i
+faction = i
+UNUSED12 = x
+expansion = i
+UNUSED14 = x
+groupId = i
+UNUSED16 = X_LOC
+
+[lock]
+id = n
+type1 = i
+type2 = i
+type3 = i
+type4 = i
+type5 = i
+UNUSED6 = x
+UNUSED7 = x
+UNUSED8 = x
+properties1 = i
+properties2 = i
+properties3 = i
+properties4 = i
+properties5 = i
+UNUSED14 = x
+UNUSED15 = x
+UNUSED16 = x
+reqSkill1 = i
+reqSkill2 = i
+reqSkill3 = i
+reqSkill4 = i
+reqSkill5 = i
+UNUSED22 = x
+UNUSED23 = x
+UNUSED24 = x
+UNUSED25 = x
+UNUSED26 = x
+UNUSED27 = x
+UNUSED28 = x
+UNUSED29 = x
+UNUSED30 = x
+UNUSED31 = x
+UNUSED32 = x
+
+[locktype]
+id = n
+name = LOC
+state = LOC
+process = LOC
+strref = s
+
+[mailtemplate]
+id = n
+subject = LOC
+text = LOC
+
+[map]
+id = n
+nameINT = s
+areaType = i
+UNUSED3 = x
+isBG = i
+name = LOC
+UNUSED6 = X_LOC
+UNUSED7 = X_LOC
+UNUSED8 = x
+UNUSED9 = x
+UNUSED10 = x
+parentMapId = i
+parentX = f
+parentY = f
+UNUSED14 = x
+expansion = i
+UNUSED16 = x
+maxPlayers = i
+
+[mapdifficulty]
+id = n
+mapId = i
+difficulty = i
+UNUSED3 = X_LOC
+UNUSED4 = x
+nPlayer = i
+nPlayerString = s
+
+[material]
+id = n
+UNUSED1 = x
+UNUSED2 = x
+sheatheSoundId = i
+unsheatheSoundId = i
+
+[npcsounds]
+id = n
+greetSoundId = i
+byeSoundId = i
+angrySoundId = i
+UNUSED4 = x
+
+[overridespelldata]
+id = n
+spellId1 = i
+spellId2 = i
+spellId3 = i
+spellId4 = i
+UNUSED5 = x
+spellId5 = i
+UNUSED7 = x
+UNUSED8 = x
+UNUSED9 = x
+UNUSED10 = x
+UNUSED11 = x
+
+[powerdisplay]
+id = n
+realType = i
+globalString = s
+r = b
+g = b
+b = b
+
+[questfactionreward]
+id = n
+field1 = i
+field2 = i
+field3 = i
+field4 = i
+field5 = i
+field6 = i
+field7 = i
+field8 = i
+field9 = i
+field10 = i
+
+[questsort]
+id = n
+name = LOC
+
+[questxp]
+id = n
+field1 = i
+field2 = i
+field3 = i
+field4 = i
+field5 = i
+field6 = i
+field7 = i
+field8 = i
+field9 = i
+field10 = i
+
+[randproppoints]
+id = n
+epic1 = i
+epic2 = i
+epic3 = i
+epic4 = i
+epic5 = i
+rare1 = i
+rare2 = i
+rare3 = i
+rare4 = i
+rare5 = i
+uncommon1 = i
+uncommon2 = i
+uncommon3 = i
+uncommon4 = i
+uncommon5 = i
+
+[scalingstatdistribution]
+id = n
+statMod1 = i
+statMod2 = i
+statMod3 = i
+statMod4 = i
+statMod5 = i
+statMod6 = i
+statMod7 = i
+statMod8 = i
+statMod9 = i
+statMod10 = i
+modifier1 = i
+modifier2 = i
+modifier3 = i
+modifier4 = i
+modifier5 = i
+modifier6 = i
+modifier7 = i
+modifier8 = i
+modifier9 = i
+modifier10 = i
+maxLevel = i
+
+[scalingstatvalues]
+UNUSED0 = x
+id = n
+shoulderMultiplier = i
+trinketMultiplier = i
+weaponMultiplier = i
+rangedMultiplier = i
+clothShoulderArmor = i
+leatherShoulderArmor = i
+mailShoulderArmor = i
+plateShoulderArmor = i
+weaponDPS1H = i
+weaponDPS2H = i
+casterDPS1H = i
+casterDPS2H = i
+rangedDPS = i
+wandDPS = i
+spellPower = i
+primBudged = i
+tertBudged = i
+clothCloakArmor = i
+clothChestArmor = i
+leatherChestArmor = i
+mailChestArmor = i
+plateChestArmor = i
+
+[screeneffect]
+id = n
+name = s
+UNUSED2 = x
+UNUSED3 = x
+UNUSED4 = x
+UNUSED5 = x
+UNUSED6 = x
+UNUSED7 = x
+soundAmbienceId = i
+zoneMusicId = i
+
+[skillline]
+id = n
+categoryId = i
+UNUSED2 = x
+name = LOC
+description = LOC
+iconId = i
+UNUSED6 = X_LOC
+UNUSED7 = x
+
+[skilllineability]
+id = n
+skillLineId = i
+spellId = i
+reqRaceMask = i
+reqClassMask = i
+UNUSED5 = x
+UNUSED6 = x
+reqSkillLevel = i
+UNUSED8 = x
+acquireMethod = i
+skillLevelGrey = i
+skillLevelYellow = i
+UNUSED12 = x
+UNUSED13 = x
+
+[skilllinecategory]
+id = n
+name = LOC
+index = i
+
+[skillraceclassinfo]
+id = n
+skillLine = i
+raceMask = i
+classMask = i
+flags = i
+reqLevel = i
+UNUSED6 = x
+UNUSED7 = x
+
+[soundambience]
+id = n
+soundIdDay = i
+soundIdNight = i
+
+[soundemitters]
+id = n
+posX = f
+posY = f
+UNUSED3 = x
+UNUSED4 = x
+UNUSED5 = x
+UNUSED6 = x
+soundId = i
+mapId = i
+UNUSED9 = x
+
+[soundentries]
+id = n
+type = i
+name = s
+file1 = s
+file2 = s
+file3 = s
+file4 = s
+file5 = s
+file6 = s
+file7 = s
+file8 = s
+file9 = s
+file10 = s
+UNUSED13 = x
+UNUSED14 = x
+UNUSED15 = x
+UNUSED16 = x
+UNUSED17 = x
+UNUSED18 = x
+UNUSED19 = x
+UNUSED20 = x
+UNUSED21 = x
+UNUSED22 = x
+path = s
+UNUSED24 = x
+flags = i
+UNUSED26 = x
+UNUSED27 = x
+UNUSED28 = x
+UNUSED29 = x
+
+[spell]
+id = n
+category = i
+dispelType = i
+mechanic = i
+attributes0 = u
+attributes1 = u
+attributes2 = u
+attributes3 = u
+attributes4 = u
+attributes5 = u
+attributes6 = u
+attributes7 = u
+stanceMask = i
+UNUSED13 = x
+stanceMaskNot = i
+UNUSED15 = x
+targets = i
+UNUSED17 = x
+spellFocus = i
+UNUSED19 = x
+UNUSED20 = x
+UNUSED21 = x
+UNUSED22 = x
+UNUSED23 = x
+UNUSED24 = x
+UNUSED25 = x
+UNUSED26 = x
+UNUSED27 = x
+castTimeId = i
+recoveryTime = i
+recoveryTimeCategory = i
+UNUSED31 = x
+UNUSED32 = x
+UNUSED33 = x
+UNUSED34 = x
+procChance = i
+procCharges = i
+maxLevel = i
+baseLevel = i
+spellLevel = i
+durationId = i
+powerType = i
+powerCost = i
+powerCostPerLevel = i
+powerPerSecond = i
+powerPerSecondPerLevel = i
+rangeId = i
+UNUSED47 = x
+UNUSED48 = x
+stackAmount = i
+tool1 = i
+tool2 = i
+reagent1 = i
+reagent2 = i
+reagent3 = i
+reagent4 = i
+reagent5 = i
+reagent6 = i
+reagent7 = i
+reagent8 = i
+reagentCount1 = i
+reagentCount2 = i
+reagentCount3 = i
+reagentCount4 = i
+reagentCount5 = i
+reagentCount6 = i
+reagentCount7 = i
+reagentCount8 = i
+equippedItemClass = i
+equippedItemSubClassMask = i
+equippedItemInventoryTypeMask = i
+effect1Id = i
+effect2Id = i
+effect3Id = i
+effect1DieSides = i
+effect2DieSides = i
+effect3DieSides = i
+effect1RealPointsPerLevel = f
+effect2RealPointsPerLevel = f
+effect3RealPointsPerLevel = f
+effect1BasePoints = i
+effect2BasePoints = i
+effect3BasePoints = i
+effect1Mechanic = i
+effect2Mechanic = i
+effect3Mechanic = i
+effect1ImplicitTargetA = i
+effect2ImplicitTargetA = i
+effect3ImplicitTargetA = i
+effect1ImplicitTargetB = i
+effect2ImplicitTargetB = i
+effect3ImplicitTargetB = i
+effect1RadiusId = i
+effect2RadiusId = i
+effect3RadiusId = i
+effect1AuraId = i
+effect2AuraId = i
+effect3AuraId = i
+effect1Periode = i
+effect2Periode = i
+effect3Periode = i
+effect1ValueMultiplier = f
+effect2ValueMultiplier = f
+effect3ValueMultiplier = f
+effect1ChainTarget = i
+effect2ChainTarget = i
+effect3ChainTarget = i
+effect1CreateItemId = i
+effect2CreateItemId = i
+effect3CreateItemId = i
+effect1MiscValue = i
+effect2MiscValue = i
+effect3MiscValue = i
+effect1MiscValueB = i
+effect2MiscValueB = i
+effect3MiscValueB = i
+effect1TriggerSpell = i
+effect2TriggerSpell = i
+effect3TriggerSpell = i
+effect1PointsPerComboPoint = f
+effect2PointsPerComboPoint = f
+effect3PointsPerComboPoint = f
+effect1SpellClassMaskA = i
+effect1SpellClassMaskB = i
+effect1SpellClassMaskC = i
+effect2SpellClassMaskA = i
+effect2SpellClassMaskB = i
+effect2SpellClassMaskC = i
+effect3SpellClassMaskA = i
+effect3SpellClassMaskB = i
+effect3SpellClassMaskC = i
+spellVisualId1 = i
+spellVisualId2 = i
+iconId = i
+iconIdActive = i
+UNUSED135 = x
+name = LOC
+rank = LOC
+description = LOC
+buff = LOC
+powerCostPercent = i
+startRecoveryCategory = i
+startRecoveryTime = i
+maxTargetLevel = i
+spellFamilyId = i
+spellFamilyFlags1 = i
+spellFamilyFlags2 = i
+spellFamilyFlags3 = i
+maxAffectedTargets = i
+damageClass = i
+UNUSED150 = x
+UNUSED151 = x
+effect1DamageMultiplier = f
+effect2DamageMultiplier = f
+effect3DamageMultiplier = f
+UNUSED155 = x
+UNUSED156 = x
+UNUSED157 = x
+toolCategory1 = i
+toolCategory2 = i
+UNUSED160 = x
+schoolMask = i
+runeCostId = i
+UNUSED163 = x
+powerDisplayId = i
+effect1BonusMultiplier = f
+effect2BonusMultiplier = f
+effect3BonusMultiplier = f
+spellDescriptionVariable = i
+spellDifficulty = i
+
+[spellcasttimes]
+id = n
+baseTime = i
+UNUSED2 = x
+UNUSED3 = x
+
+[spelldescriptionvariables]
+id = n
+vars = s
+
+[spellduration]
+id = n
+baseTime = i
+UNUSED2 = x
+UNUSED3 = x
+
+[spelldifficulty]
+UNUSED0 = x
+normal10 = i
+normal25 = i
+heroic10 = i
+heroic25 = i
+
+[spellfocusobject]
+id = n
+name = LOC
+
+[spellicon]
+id = n
+iconPath = s
+
+[spellitemenchantment]
+id = n
+charges = i
+type1 = i
+type2 = i
+type3 = i
+amount1 = i
+amount2 = i
+amount3 = i
+UNUSED8 = x
+UNUSED9 = x
+UNUSED10 = x
+object1 = i
+object2 = i
+object3 = i
+name = LOC
+UNUSED15 = x
+UNUSED16 = x
+UNUSED17 = x
+conditionId = i
+skillLine = i
+skillLevel = i
+requiredLevel = i
+
+[spellitemenchantmentcondition]
+id = n
+color1 = b
+color2 = b
+color3 = b
+color4 = b
+color5 = b
+UNUSED6 = x
+UNUSED7 = x
+UNUSED8 = x
+UNUSED9 = x
+UNUSED10 = x
+comparator1 = b
+comparator2 = b
+comparator3 = b
+comparator4 = b
+comparator5 = b
+cmpColor1 = b
+cmpColor2 = b
+cmpColor3 = b
+cmpColor4 = b
+cmpColor5 = b
+value1 = i
+value2 = i
+value3 = i
+value4 = i
+value5 = i
+UNUSED26 = X
+UNUSED27 = X
+UNUSED28 = X
+UNUSED29 = X
+UNUSED30 = X
+
+[spellradius]
+id = n
+radiusMin = f
+UNUSED2 = x
+radiusMax = f
+
+[spellrange]
+id = n
+rangeMinHostile = f
+rangeMinFriend = f
+rangeMaxHostile = f
+rangeMaxFriend = f
+rangeType = i
+name = LOC
+UNUSED7 = X_LOC
+
+[spellrunecost]
+id = n
+costBlood = i
+costUnholy = i
+costFrost = i
+runicPowerGain = i
+
+[spellshapeshiftform]
+id = n
+UNUSED1 = x
+name = LOC
+flags = i
+creatureType = i
+UNUSED5 = x
+UNUSED6 = x
+displayIdA = i
+displayIdH = i
+UNUSED9 = x
+UNUSED10 = x
+spellId1 = i
+spellId2 = i
+spellId3 = i
+spellId4 = i
+spellId5 = i
+spellId6 = i
+spellId7 = i
+spellId8 = i
+
+[spellvisual]
+id = n
+precastKitId = i
+castKitId = i
+impactKitId = i
+stateKitId = i
+statedoneKitId = i
+channelKitId = i
+UNUSED7 = x
+UNUSED8 = x
+UNUSED9 = x
+UNUSED10 = x
+missileSoundId = i
+animationSoundId = i
+UNUSED13 = x
+casterImpactKitId = i
+targetImpactKitId = i
+UNUSED16 = x
+UNUSED17 = x
+UNUSED18 = x
+UNUSED19 = x
+UNUSED20 = x
+UNUSED21 = x
+missileTargetingKitId = i
+instantAreaKitId = i
+impactAreaKitId = i
+persistentAreaKitId = i
+UNUSED26 = x
+UNUSED27 = x
+UNUSED28 = x
+UNUSED29 = x
+UNUSED30 = x
+UNUSED31 = x
+
+[spellvisualkit]
+id = n
+UNUSED1 = x
+UNUSED2 = x
+UNUSED3 = x
+UNUSED4 = x
+UNUSED5 = x
+UNUSED6 = x
+UNUSED7 = x
+UNUSED8 = x
+UNUSED9 = x
+UNUSED10 = x
+UNUSED11 = x
+UNUSED12 = x
+UNUSED13 = x
+UNUSED14 = x
+soundId = i
+UNUSED16 = X_LOC
+UNUSED17 = x
+UNUSED18 = x
+UNUSED19 = x
+UNUSED20 = x
+UNUSED21 = x
+
+[summonproperties]
+id = n
+control = u
+faction = x
+title = x
+slot = u
+flags = x
+
+[talent]
+id = n
+tabId = i
+row = i
+column = i
+rank1 = i
+rank2 = i
+rank3 = i
+rank4 = i
+rank5 = i
+UNUSED9 = x
+UNUSED10 = x
+UNUSED11 = x
+UNUSED12 = x
+reqTalent = i
+UNUSED14 = x
+UNUSED15 = x
+reqRank = i
+UNUSED17 = x
+UNUSED18 = x
+talentSpell = i
+UNUSED20 = x
+petCategory1 = i
+petCategory2 = i
+
+[talenttab]
+id = n
+name = LOC
+iconId = i
+raceMask = i
+classMask = i
+creatureFamilyMask = i
+tabNumber = i
+textureFile = s
+
+[taxinodes]
+id = n
+mapId = i
+posX = f
+posY = f
+UNUSED4 = x
+name = LOC
+UNUSED6 = x
+UNUSED7 = x
+
+[taxipath]
+id = n
+startNodeId = i
+endNodeId = i
+UNUSED3 = x
+
+[taxipathnode]
+id = n
+pathId = i
+nodeIdx = i
+mapId = i
+posX = f
+posY = f
+UNUSED6 = x
+UNUSED7 = x
+UNUSED8 = x
+UNUSED9 = x
+UNUSED10 = x
+
+[totemcategory]
+id = n
+name = LOC
+category = i
+categoryMask = u
+
+[vocaluisounds]
+id = n
+UNUSED1 = x
+raceId = i
+soundIdMale = i
+soundIdFemale = i
+UNUSED5 = x
+UNUSED6 = x
+
+[weaponimpactsounds]
+id = n
+subClass = i
+UNUSED2 = x
+hit1 = i
+hit2 = i
+hit3 = i
+hit4 = i
+hit5 = i
+hit6 = i
+hit7 = i
+hit8 = i
+hit9 = i
+hit10 = i
+crit1 = i
+crit2 = i
+crit3 = i
+crit4 = i
+crit5 = i
+crit6 = i
+crit7 = i
+crit8 = i
+crit9 = i
+crit10 = i
+
+[weaponswingsounds2]
+id = n
+weaponSize = i
+UNUSED2 = x
+soundId = i
+
+[worldmaparea]
+id = n
+mapId = i
+areaId = i
+nameINT = s
+left = f
+right = f
+top = f
+bottom = f
+displayMapId = x
+defaultDungeonMapId = i
+parentWorldMapId = x
+
+[worldmapoverlay]
+id = n
+worldMapAreaId = i
+areaTableId = i
+UNUSED3 = x
+UNUSED4 = x
+UNUSED5 = x
+UNUSED6 = x
+UNUSED7 = x
+textureString = s
+w = i
+h = i
+x = i
+y = i
+UNUSED13 = x
+UNUSED14 = x
+UNUSED15 = x
+UNUSED16 = x
+
+[worldmaptransforms]
+id = n
+sourceMapId = i
+minX = f
+minY = f
+maxX = f
+maxY = f
+targetMapId = i
+offsetX = f
+offsetY = f
+dungeonMapId = i
+
+[worldstatezonesounds]
+stateId = i
+value = i
+areaId = i
+wmoAreaId = i
+zoneIntroMusicId = i
+zoneMusicId = i
+soundAmbienceId = i
+UNUSED7 = x
+
+[zoneintromusictable]
+id = n
+UNUSED1 = x
+soundId = i
+UNUSED3 = x
+UNUSED4 = x
+
+[zonemusic]
+id = n
+UNUSED1 = x
+UNUSED2 = x
+UNUSED3 = x
+UNUSED4 = x
+UNUSED5 = x
+soundIdDay = i
+soundIdNight = i
diff --git a/setup/tools/dbc/13623.ini b/setup/tools/dbc/13623.ini
new file mode 100644
index 00000000..da23181f
--- /dev/null
+++ b/setup/tools/dbc/13623.ini
@@ -0,0 +1,89 @@
+; DBC structure - 4.0.6.13623
+;
+; x - not used/unknown, 4 bytes
+; X - not used/unknown, 1 byte
+; s - string block index, 4 bytes
+; S - string block index, 4 bytes - localized; autofill
+; f - float, 4 bytes (rounded to 4 digits after comma)
+; u - unsigned int, 4 bytes
+; i - signed int, 4 bytes
+; b - unsigned char, 1 byte
+; d - sorted by this field, not included in array
+; n - same, but field included in array
+;
+; LOC - used locale strings macro [sxsssxsxsxxxxxxxx]
+; X_LOC - unused locale strings macro [xxxxxxxxxxxxxxxxx]
+
+[areatable]
+id = n
+mapId = i
+areaTable = i
+areaBit = x
+flags = i
+UNK1 = i
+soundProviderPref = x
+soundProviderPrefWater = x
+soundAmbience = i
+zoneMusic = i
+nameINT = s
+zoneIntroMusic = i
+explorationLevel = x
+name = S
+factionGroupMask = i
+liquidType1 = x
+liquidType2 = x
+liquidType3 = x
+liquidType4 = x
+minElevation = x
+ambientMultiplier = x
+lightId = x
+mountFLags = x
+uwIntroSound = x
+uwZoneMusic = x
+uwAmbience = x
+worldPvpId = x
+pvpCombatWorldStateId = x
+
+[dungeonmap]
+id = n
+mapId = i
+floor = i
+minY = f ; maxY ?
+maxY = f ; maxX ?
+minX = f ; minY ?
+maxX = f ; minX ?
+worldMapAreaId = i
+
+[worldmaparea]
+id = n
+mapId = i
+areaId = i
+nameINT = s
+left = f
+right = f
+top = f
+bottom = f
+displayMapId = x
+defaultDungeonMapId = i
+parentWorldMapId = x
+flags = i
+levelMin = x
+levelMax = x
+
+[worldmapoverlay]
+id = n
+worldMapAreaId = i
+areaTableId = i
+UNUSED3 = x
+UNUSED4 = x
+UNUSED5 = x
+textureString = s
+w = i
+h = i
+x = i
+y = i
+UNUSED11 = x
+UNUSED12 = x
+UNUSED13 = x
+UNUSED14 = x
+playerConditionId = x
diff --git a/setup/tools/dbc/15595.ini b/setup/tools/dbc/15595.ini
new file mode 100644
index 00000000..9a11de6e
--- /dev/null
+++ b/setup/tools/dbc/15595.ini
@@ -0,0 +1,2177 @@
+; DBC structure - 4.3.4.15595
+;
+; x - not used/unknown, 4 bytes
+; X - not used/unknown, 1 byte
+; s - string block index, 4 bytes
+; S - string block index, 4 bytes - localized; autofill
+; f - float, 4 bytes (rounded to 4 digits after comma)
+; u - unsigned int, 4 bytes
+; i - signed int, 4 bytes
+; b - unsigned char, 1 byte
+; d - sorted by this field, not included in array
+; n - same, but field included in array
+;
+; LOC - used locale strings macro [sxsssxsxsxxxxxxxx]
+; X_LOC - unused locale strings macro [xxxxxxxxxxxxxxxxx]
+
+[areatable]
+id = n
+mapId = i
+areaTable = i
+areaBit = x
+flags = i
+soundProviderPref = x
+soundProviderPrefWater = x
+soundAmbience = i
+zoneMusic = i
+introSound = x
+; nameINT = s
+; zoneIntroMusic = i
+explorationLevel = x
+name = S
+factionGroupMask = i
+liquidType1 = x
+liquidType2 = x
+liquidType3 = x
+liquidType4 = x
+minElevation = x
+ambientMultiplier = x
+lightId = x
+mountFLags = x
+uwIntroSound = x
+uwZoneMusic = x
+uwAmbience = x
+worldPvpId = x
+pvpCombatWorldStateId = x
+
+[dungeonmap]
+id = n
+mapId = i
+floor = i
+minX = f
+maxX = f
+minY = f
+maxY = f
+worldMapAreaId = i
+
+[worldmaparea]
+id = n
+mapId = i
+areaId = i
+nameINT = s
+left = f
+right = f
+top = f
+bottom = f
+displayMapId = x
+defaultDungeonMapId = i
+parentWorldMapId = x
+flags = i
+levelMin = x
+levelMax = x
+
+[worldmapoverlay]
+id = n
+worldMapAreaId = i
+areaTableId = i
+areaTableId2 = x
+areaTableId3 = x
+areaTableId4 = x
+textureString = s
+w = i
+h = i
+x = i
+y = i
+hitRectTop = x
+hitRectLeft = x
+hitRectBottom = x
+hitRectRight = x
+
+; from TrnityCore - Cataclysm Preservation Project
+; Achievementfmt = "niixsxiixixxii";
+; AnimKitfmt = "nxx";
+; AchievementCriteriafmt = "niiiliiiisiiiiiiiiiiiii";
+; AreaTableEntryfmt = "niiiiiiiiiisiiiiiffiiiiiii";
+; AreaGroupEntryfmt = "niiiiiii";
+; AreaPOIEntryfmt = "nxiiiiiiiiixffixixxixx";
+; AreaTriggerEntryfmt = "nifffiiifffff";
+; ArmorLocationfmt = "nfffff";
+; AuctionHouseEntryfmt = "niiix";
+; BankBagSlotPricesEntryfmt = "ni";
+; BannedAddOnsfmt = "nxxxxxxxxxx";
+; BarberShopStyleEntryfmt = "nixxxiii";
+; BattlemasterListEntryfmt = "niiiiiiiiixsiiiixxxx";
+; CharStartOutfitEntryfmt = "dbbbXiiiiiiiiiiiiiiiiiiiiiiiixxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxii";
+; CharSectionsEntryfmt = "diiixxxiii";
+; CharTitlesEntryfmt = "nxssix";
+; ChatChannelsEntryfmt = "nixsx";
+; ChrClassesEntryfmt = "nixsxxxixiiiii";
+; ChrRacesEntryfmt = "niixiixixxxxixsxxxxxixxx";
+; ChrClassesXPowerTypesfmt = "nii";
+; CinematicCameraEntryfmt = "nsiffff";
+; CinematicSequencesEntryfmt = "nxiiiiiiii";
+; CreatureDisplayInfofmt = "nixifxxxxxxxxxxxx";
+; CreatureDisplayInfoExtrafmt = "diixxxxxxxxxxxxxxxxxx";
+; CreatureModelDatafmt = "nisxfxxxxxxxxxxffxxxxxxxxxxxxxf";
+; CreatureFamilyfmt = "nfifiiiiixsx";
+; CreatureSpellDatafmt = "niiiixxxx";
+; CreatureTypefmt = "nxx";
+; CurrencyTypesfmt = "nixxxxiiiix";
+; DestructibleModelDatafmt = "ixxixxxixxxixxxixxxxxxxx";
+; DungeonEncounterfmt = "niixisxx";
+; DurabilityCostsfmt = "niiiiiiiiiiiiiiiiiiiiiiiiiiiii";
+; DurabilityQualityfmt = "nf";
+; EmotesEntryfmt = "nxxiiixx";
+; EmotesTextEntryfmt = "nxixxxxxxxxxxxxxxxx";
+; EmotesTextSoundEntryfmt = "niiii";
+; FactionEntryfmt = "niiiiiiiiiiiiiiiiiiffiisxi";
+; FactionTemplateEntryfmt = "niiiiiiiiiiiii";
+; GameObjectArtKitfmt = "nxxxxxxx";
+; GameObjectDisplayInfofmt = "nsxxxxxxxxxxffffffxxx";
+; GemPropertiesEntryfmt = "nixxii";
+; GlyphPropertiesfmt = "niii";
+; GlyphSlotfmt = "nii";
+; GtBarberShopCostBasefmt = "xf";
+; GtCombatRatingsfmt = "xf";
+; GtOCTHpPerStaminafmt = "df";
+; GtChanceToMeleeCritBasefmt = "xf";
+; GtChanceToMeleeCritfmt = "xf";
+; GtChanceToSpellCritBasefmt = "xf";
+; GtChanceToSpellCritfmt = "xf";
+; GtNPCManaCostScalerfmt = "xf";
+; GtOCTClassCombatRatingScalarfmt = "df";
+; GtOCTRegenHPfmt = "f";
+; GtOCTRegenMPfmt = "f";
+; GtRegenMPPerSptfmt = "xf";
+; GtSpellScalingfmt = "df";
+; GtOCTBaseHPByClassfmt = "df";
+; GtOCTBaseMPByClassfmt = "df";
+; GuildPerkSpellsfmt = "dii";
+; Holidaysfmt = "niiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiixxsiix";
+; ImportPriceArmorfmt = "nffff";
+; ImportPriceQualityfmt = "nf";
+; ImportPriceShieldfmt = "nf";
+; ImportPriceWeaponfmt = "nf";
+; ItemPriceBasefmt = "diff";
+; ItemReforgefmt = "nifif";
+; ItemBagFamilyfmt = "nx";
+; ItemArmorQualityfmt = "nfffffffi";
+; ItemArmorShieldfmt = "nifffffff";
+; ItemArmorTotalfmt = "niffff";
+; ItemClassfmt = "dixxfx";
+; ItemDamagefmt = "nfffffffi";
+; ItemDisenchantLootfmt = "niiiiii";
+; ItemDisplayTemplateEntryfmt = "nxxxxxxxxxxixxxxxxxxxxx";
+; ItemLimitCategoryEntryfmt = "nxii";
+; ItemRandomPropertiesfmt = "nxiiiiis";
+; ItemRandomSuffixfmt = "nsxiiiiiiiiii";
+; ItemSetEntryfmt = "dsiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii";
+; LFGDungeonEntryfmt = "nsiiiiiiiiixxixixiiii";
+; LFGDungeonsGroupingMapfmt = "niii";
+; LightEntryfmt = "nifffxxxxxxxxxx";
+; LiquidTypefmt = "nxxixixxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";
+; LockEntryfmt = "niiiiiiiiiiiiiiiiiiiiiiiixxxxxxxx";
+; PhaseEntryfmt = "nsi";
+; PhaseGroupfmt = "nii";
+; MailTemplateEntryfmt = "nxs";
+; MapEntryfmt = "nsiiiisissififfiiiii";
+; MapDifficultyEntryfmt = "diisiix";
+; MovieEntryfmt = "nxxx";
+; NamesProfanityEntryfmt = "dsi";
+; NamesReservedEntryfmt = "dsi";
+; MountCapabilityfmt = "niiiiiii";
+; MountTypefmt = "niiiiiiiiiiiiiiiiiiiiiiii";
+; NameGenfmt = "dsii";
+; NumTalentsAtLevelfmt = "df";
+; OverrideSpellDatafmt = "niiiiiiiiiixx";
+; QuestFactionRewardfmt = "niiiiiiiiii";
+; QuestPOIBlobfmt = "niii";
+; QuestPOIPointfmt = "niii";
+; QuestSortEntryfmt = "nx";
+; QuestXPfmt = "niiiiiiiiii";
+; PlayerConditionfmt = "ns";
+; PowerDisplayfmt = "nixxxx";
+; PvPDifficultyfmt = "diiiii";
+; RandomPropertiesPointsfmt = "niiiiiiiiiiiiiii";
+; ResearchBranchEntryfmt = "nsiisi";
+; ResearchFieldEntryfmt = "nsi";
+; ResearchProjectEntryfmt = "nssiiiisi";
+; ResearchSiteEntryfmt = "niiss";
+; ScalingStatDistributionfmt = "niiiiiiiiiiiiiiiiiiiiii";
+; ScalingStatValuesfmt = "iniiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii";
+; SkillLinefmt = "nisxixi";
+; SkillLineAbilityfmt = "niiiixxiiiiiii";
+; SkillRaceClassInfofmt = "diiiiixix";
+; SkillTiersfmt = "nxxxxxxxxxxxxxxxxiiiiiiiiiiiiiiii";
+; SoundEntriesfmt = "nxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";
+; SpellCastTimefmt = "nixx";
+; SpellCategoriesEntryfmt = "diiiiii";
+; SpellCategoryfmt = "niix";
+; SpellDifficultyfmt = "niiii";
+; SpellDurationfmt = "niii";
+; SpellEffectEntryfmt = "nifiiiffiiiiiifiifiiiiiiiix";
+; SpellEntryfmt = "niiiiiiiiiiiiiiifiiiissxxiixxifiiiiiiixiiiiiiiii";
+; SpellFocusObjectfmt = "nx";
+; SpellItemEnchantmentfmt = "nxiiiiiixxxiiisiiiiiiix";
+; SpellItemEnchantmentConditionfmt = "nbbbXxxxxxxbbbXXbbbxiiixxXXXXXX";
+; SpellRadiusfmt = "nfff";
+; SpellRangefmt = "nffffixx";
+; SpellReagentsEntryfmt = "diiiiiiiiiiiiiiii";
+; SpellScalingEntryfmt = "diiiiffffffffffi";
+; SpellTotemsEntryfmt = "niiii";
+; SpellTargetRestrictionsEntryfmt = "nfiiii";
+; SpellPowerEntryfmt = "diiiixxf";
+; SpellInterruptsEntryfmt = "diiiii";
+; SpellEquippedItemsEntryfmt = "diii";
+; SpellAuraOptionsEntryfmt = "niiii";
+; SpellAuraRestrictionsEntryfmt = "diiiiiiii";
+; SpellCastingRequirementsEntryfmt = "niiiiii";
+; SpellClassOptionsEntryfmt = "dxiiiix";
+; SpellCooldownsEntryfmt = "diii";
+; SpellLevelsEntryfmt = "diii";
+; SpellRuneCostfmt = "niiii";
+; SpellShapeshiftEntryfmt = "niiiix";
+; SpellShapeshiftFormfmt = "nxxiixiiiiiiiiiiiiiix";
+; SpellVisualfmt = "dxxxxxxiixxxxxxxxxxxxxxxxxxxxxxxi";
+; SpellVisualKitfmt = "niiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii";
+; StableSlotPricesfmt = "ni";
+; SummonPropertiesfmt = "niiiii";
+; TalentEntryfmt = "niiiiiiiiiiiiiixxxx";
+; TalentTabEntryfmt = "nxxiiixxxii";
+; TalentTreePrimarySpellsfmt = "diix";
+; TaxiNodesEntryfmt = "nifffsiiiff";
+; TaxiPathEntryfmt = "niii";
+; TaxiPathNodeEntryfmt = "diiifffiiii";
+; TotemCategoryEntryfmt = "nxii";
+; UnitPowerBarfmt = "niiixffxxxxxxxxxxxxxxxxxxxx";
+; TransportAnimationfmt = "diifffx";
+; TransportRotationfmt = "diiffff";
+; VehicleEntryfmt = "niffffiiiiiiiifffffffffffffffssssfifiiii";
+; VehicleSeatEntryfmt = "niiffffffffffiiiiiifffffffiiifffiiiiiiiffiiiiiffffffffffffiiiiiiii";
+; WMOAreaTableEntryfmt = "niiixxxxxiixxxx";
+; WorldSafeLocsEntryfmt = "nifffx";
+
+; struct AchievementEntry
+; {
+; uint32 ID; // 0
+; int32 Faction; // 1 -1=all, 0=horde, 1=alliance
+; int32 MapID; // 2 -1=none
+; //uint32 Supercedes; // 3 its Achievement parent (can`t start while parent uncomplete, use its Criteria if don`t have own, use its progress on begin)
+; char* Title; // 4
+; //char* Description; // 5
+; uint32 Category; // 6
+; uint32 Points; // 7 reward points
+; //uint32 Ui_order; // 8
+; uint32 Flags; // 9
+; //uint32 IconID; // 10 icon (from SpellIcon.dbc)
+; //char* Reward; // 11
+; uint32 MinimumCriteria; // 12 - need this count of completed criterias (own or referenced achievement criterias)
+; uint32 SharesCriteria; // 13 - referenced achievement (counting of all completed criterias)
+; };
+
+; struct AchievementCategoryEntry
+; {
+; uint32 ID; // 0
+; uint32 Parent; // 1 -1 for main category
+; //char* Name; // 2
+; //uint32 Ui_order; // 3
+; };
+
+; struct AchievementCriteriaEntry
+; {
+; uint32 ID; // 0
+; uint32 AchievementID; // 1
+; uint32 Type; // 2
+; uint64 Quantity; // 4
+; uint32 StartEvent; // 5
+; int32 StartAsset; // 6
+; uint32 FailEvent; // 7
+; int32 FailAsset; // 8
+; char* Description; // 9
+; uint32 Flags; // 10
+; uint32 TimerStartEvent; // 11
+; uint32 TimerAsset; // 12
+; uint32 TimerTime; // 13
+; uint32 OrderIndex; // 14
+; uint32 RequiredWorldStateID; // 15
+; int32 RequiredWorldStateValue; // 16
+; uint32 AdditionalConditionType[3]; // 17-19
+; uint32 AdditionalConditionValue[3]; // 20-22
+; };
+
+; struct AnimKitEntry
+; {
+; uint32 ID; // 0
+; //uint32 OneShotDuration; // 1
+; //uint32 OneShotStopAnimKitID; // 2
+; };
+
+; struct AreaGroupEntry
+; {
+; uint32 ID; // 0
+; uint32 AreaID[6]; // 1-6
+; uint32 NextAreaID; // 7 index of next group
+; };
+
+; struct AreaPOIEntry
+; {
+; uint32 ID; // 0
+; // uint32 Importance // 1
+; uint32 Icon[9]; // 2-10
+; // uint32 FactionID // 11
+; DBCPosition2D Pos; // 12 - 13
+; uint32 ContinentID; // 14
+; // uint32 Flags; // 15
+; uint32 AreaID; // 16
+; //char* Name; // 17
+; //char* Description; // 18
+; uint32 WorldStateID; // 19
+; //uint32 WorldMapLink; // 20
+; //uint32 PortLocID; // 21
+; };
+
+; struct AreaTriggerEntry
+; {
+; uint32 ID; // 0
+; uint32 ContinentID; // 1
+; DBCPosition3D Pos; // 2 - 4
+; uint32 PhaseUseFlags; // 5
+; uint32 PhaseID; // 6
+; uint32 PhaseGroupID; // 7
+; float Radius; // 8
+; float Box_length; // 9
+; float Box_width; // 10
+; float Box_height; // 11
+; float Box_yaw; // 12
+; };
+
+; struct ArmorLocationEntry
+; {
+; uint32 ID; // 0
+; float Value[5]; // 1-5 multiplier for armor types (cloth...plate, no armor?)
+; };
+
+; struct AuctionHouseEntry
+; {
+; uint32 ID; // 0 index
+; uint32 FactionID; // 1 id of faction.dbc for player factions associated with city
+; uint32 DepositRate; // 2 1/3 from real
+; uint32 ConsignmentRate; // 3
+; //char* Name; // 4
+; };
+
+; struct BankBagSlotPricesEntry
+; {
+; uint32 ID; // 0
+; uint32 Cost; // 1
+; };
+
+; struct BannedAddOnsEntry
+; {
+; uint32 ID; // 0
+; // uint32 NameMD5[4]; // 1 - 4
+; // uint32 VersionMD5[4]; // 5 - 8
+; // uint32 LastModified; // 9
+; // uint32 Flags; // 10
+; };
+
+; struct BarberShopStyleEntry
+; {
+; uint32 ID; // 0
+; uint32 Type; // 1 value 0 -> hair, value 2 -> facialhair
+; //char* DisplayName; // 2
+; //uint32 Description; // 3
+; //float Cost_Modifier; // 4
+; uint32 Race; // 5
+; uint32 Sex; // 6
+; uint32 Data; // 7
+; };
+
+; struct BattlemasterListEntry
+; {
+; uint32 ID; // 0
+; int32 MapID[8]; // 1-8
+; uint32 InstanceType; // 9 map type (3 - BG, 4 - arena)
+; //uint32 GroupsAllowed; // 10 (0 or 1)
+; char* Name; // 11
+; uint32 MaxGroupSize; // 12 maxGroupSize, used for checking if queue as group
+; uint32 HolidayWorldState; // 13 new 3.1
+; uint32 MinLevel; // 14, min level (sync with PvPDifficulty.dbc content)
+; uint32 MaxLevel; // 15, max level (sync with PvPDifficulty.dbc content)
+; //uint32 RatedPlayers; // 16 4.0.1
+; //uint32 MinPlayers; // 17 - 4.0.6.13596
+; //uint32 MaxPlayers; // 18 4.0.1
+; //uint32 Flags; // 19 4.0.3, value 2 for Rated Battlegrounds
+; };
+
+; struct CharStartOutfitEntry
+; {
+; //uint32 ID; // 0
+; uint8 RaceID; // 1
+; uint8 ClassID; // 2
+; uint8 SexID; // 3
+; //uint8 OutfitID; // 4
+; int32 ItemID[24]; // 5-28
+; //int32 DisplayItemID[24]; // 29-52 not required at server side
+; //int32 InventoryType[24]; // 53-76 not required at server side
+; uint32 PetDisplayID; // 77 Pet Model ID for starting pet
+; uint32 PetFamilyID; // 78 Pet Family Entry for starting pet
+; };
+
+; struct CharSectionsEntry
+; {
+; //uint32 ID // 0
+; uint32 RaceID; // 1
+; uint32 SexID; // 2
+; uint32 BaseSection; // 3
+; //char* TextureName[3]; // 4 - 7
+; uint32 Flags; // 8
+; uint32 VariationIndex; // 9
+; uint32 ColorIndex; // 10
+; };
+
+; struct CharTitlesEntry
+; {
+; uint32 ID; // 0 title ids, for example in Quest::GetCharTitleId()
+; //uint32 Condition_ID; // 1
+; char* Name; // 2
+; char* Name1; // 3
+; uint32 Mask_ID; // 4 used in PLAYER_CHOSEN_TITLE and 1<[21] + ArmorSubClassCost<32>[8]
+; };
+
+; struct DurabilityQualityEntry
+; {
+; uint32 ID; // 0
+; float Data; // 1
+; };
+
+; struct EmotesEntry
+; {
+; uint32 ID; // 0
+; //char* EmoteSlashCommand; // 1, internal name
+; //uint32 AnimID; // 2, ref to animationData
+; uint32 EmoteFlags; // 3, bitmask, may be unit_flags
+; uint32 EmoteSpecProc; // 4, Can be 0, 1 or 2 (determine how emote are shown)
+; uint32 EmoteSpecProcParam; // 5, uncomfirmed, may be enum UnitStandStateType
+; //uint32 EventSoundID; // 6, ref to soundEntries
+; //uint32 SpellVisualKitID // 7
+; };
+
+; struct EmotesTextEntry
+; {
+; uint32 ID; // 0
+; // char* Name; // 1
+; uint32 EmoteID; // 2
+; // uint32 EmoteText[16]; // 3 - 18
+; };
+
+; struct EmotesTextSoundEntry
+; {
+; uint32 ID; // 0
+; uint32 EmotesTextID; // 1
+; uint32 RaceID; // 2
+; uint32 SexID; // 3 0 male / 1 female
+; uint32 SoundID; // 4
+; };
+
+; struct FactionEntry
+; {
+; uint32 ID; // 0
+; int32 ReputationIndex; // 1
+; uint32 ReputationRaceMask[4]; // 2 - 5
+; uint32 ReputationClassMask[4]; // 6 - 9
+; int32 ReputationBase[4]; // 10 - 13
+; uint32 ReputationFlags[4]; // 14 - 17
+; uint32 ParentFactionID; // 18
+; float ParentFactionMod[2]; // 19 - 20 Faction gains incoming rep * spilloverRateIn and Faction outputs rep * spilloverRateOut as spillover reputation
+; uint32 ParentFactionCap[2]; // 21 - 22 The highest rank the faction will profit from incoming spillover and It does not seem to be the max standing at which a faction outputs spillover ...so no idea
+; char* Name; // 23
+; // char* Description; // 24
+; uint32 Expansion; // 25
+; };
+
+; struct FactionTemplateEntry
+; {
+; uint32 ID; // 0
+; uint32 Faction; // 1
+; uint32 Flags; // 2
+; uint32 FactionGroup; // 3
+; uint32 FriendGroup; // 4
+; uint32 EnemyGroup; // 5
+; uint32 Enemies[4]; // 6
+; uint32 Friend[4]; // 10
+; };
+
+; struct GameObjectArtKitEntry
+; {
+; uint32 ID; // 0
+; //char* TextureVariation[3] // 1-3
+; //char* AttachModel[4] // 4-8
+; };
+
+; struct GameObjectDisplayInfoEntry
+; {
+; uint32 ID; // 0
+; char* ModelName; // 1
+; // uint32 Sound[10]; // 2 - 11
+; DBCPosition3D GeoBoxMin; // 12 - 14
+; DBCPosition3D GeoBoxMax; // 15 - 17
+; // uint32 ObjectEffectPackageID; // 18
+; // float OverrideLootEffectScale; // 19
+; // float OverrideNameScale; // 20
+; };
+
+; struct GemPropertiesEntry
+; {
+; uint32 ID; // 0
+; uint32 Enchant_ID; // 1
+; // uint32 Maxcount_inv; // 2
+; // uint32 Maxcount_item; // 3
+; uint32 Type; // 4
+; uint32 Min_item_level; // 5
+; };
+
+; struct GlyphPropertiesEntry
+; {
+; uint32 ID; // 0
+; uint32 SpellID; // 1
+; uint32 GlyphSlotFlags; // 2
+; uint32 SpellIconID; // 3 GlyphIconId (SpellIcon.dbc)
+; };
+
+; struct GlyphSlotEntry
+; {
+; uint32 ID; // 0
+; uint32 Type; // 1
+; uint32 Tooltip; // 2
+; };
+
+; // All Gt* DBC store data for 100 levels, some by 100 per class/race
+; #define GT_MAX_LEVEL 100
+; // gtOCTClassCombatRatingScalar.dbc stores data for 32 ratings, look at MAX_COMBAT_RATING for real used amount
+; #define GT_MAX_RATING 32
+;
+; struct GtBarberShopCostBaseEntry
+; {
+; //uint32 level;
+; float cost;
+; };
+
+; struct GtCombatRatingsEntry
+; {
+; //uint32 level;
+; float ratio;
+; };
+
+; struct GtChanceToMeleeCritBaseEntry
+; {
+; //uint32 level;
+; float base;
+; };
+
+; struct GtChanceToMeleeCritEntry
+; {
+; //uint32 level;
+; float ratio;
+; };
+
+; struct GtChanceToSpellCritBaseEntry
+; {
+; float base;
+; };
+
+; struct GtNPCManaCostScalerEntry
+; {
+; float ratio;
+; };
+
+; struct GtChanceToSpellCritEntry
+; {
+; float ratio;
+; };
+
+; struct GtOCTClassCombatRatingScalarEntry
+; {
+; float ratio;
+; };
+
+; struct GtOCTRegenHPEntry
+; {
+; float ratio;
+; };
+
+; struct GtOCTRegenMPEntry
+; {
+; float ratio;
+; };
+
+; struct gtOCTHpPerStaminaEntry
+; {
+; float ratio;
+; };
+
+; struct GtRegenHPPerSptEntry
+; {
+; float ratio;
+; };
+
+; struct GtRegenMPPerSptEntry
+; {
+; float ratio;
+; };
+
+; struct GtSpellScalingEntry
+; {
+; float value;
+; };
+
+; struct GtOCTBaseHPByClassEntry
+; {
+; float ratio;
+; };
+
+; struct GtOCTBaseMPByClassEntry
+; {
+; float ratio;
+; };
+
+; struct GuildPerkSpellsEntry
+; {
+; // uint32 ID; // 0
+; uint32 GuildLevel; // 1
+; uint32 SpellID; // 2
+; };
+
+; /* not used
+; struct HolidayDescriptionsEntry
+; {
+; uint32 ID; // 0
+; //char* Description // 1
+; };
+*/
+
+; struct HolidayNamesEntry
+; {
+; uint32 ID; // 0
+; //char* Name // 1
+; };
+
+; struct HolidaysEntry
+; {
+; uint32 ID; // 0
+; uint32 Duration[10]; // 1-10
+; uint32 Date[26]; // 11-36 (dates in unix time starting at January, 1, 2000)
+; uint32 Region; // 37 (wow region)
+; uint32 Looping; // 38
+; uint32 CalendarFlags[10]; // 39-48
+; //uint32 HolidayNameID; // 49 (HolidayNames.dbc)
+; //uint32 HolidayDescriptionID; // 50 (HolidayDescriptions.dbc)
+; char* TextureFilename; // 51
+; uint32 Priority; // 52
+; int32 CalendarFilterType; // 53 (-1 = Fishing Contest, 0 = Unk, 1 = Darkmoon Festival, 2 = Yearly holiday)
+; //uint32 Flags; // 54 (0 = Darkmoon Faire, Fishing Contest and Wotlk Launch, rest is 1)
+; };
+
+; // ImportPriceArmor.dbc
+; struct ImportPriceArmorEntry
+; {
+; uint32 ID; // 0 Id/InventoryType
+; float ClothModifier; // 1 Price factor cloth
+; float LeatherModifier; // 2 Price factor leather
+; float ChainModifier; // 3 Price factor mail
+; float PlateModifier; // 4 Price factor plate
+; };
+
+; // ImportPriceQuality.dbc
+; struct ImportPriceQualityEntry
+; {
+; uint32 QualityId; // 1 Quality Id (+1?)
+; float Factor; // 2 Price factor
+; };
+
+; // ImportPriceShield.dbc
+; struct ImportPriceShieldEntry
+; {
+; uint32 ID; // 1
+; float Data; // 2 Price factor
+; };
+
+; // ImportPriceWeapon.dbc
+; struct ImportPriceWeaponEntry
+; {
+; uint32 ID; // 1 Unk id (mainhand - 0, offhand - 1, weapon - 2, 2hweapon - 3, ranged/rangedright/relic - 4)
+; float Data; // 2 Price factor
+; };
+
+; // ItemPriceBase.dbc
+; struct ItemPriceBaseEntry
+; {
+; // uint32 ID; // 0
+; uint32 ItemLevel; // 1 Item level (1 - 1000)
+; float Armor; // 2 Price factor for armor
+; float Weapon; // 3 Price factor for weapons
+; };
+
+; struct ItemReforgeEntry
+; {
+; uint32 ID; // 0
+; uint32 Source_stat; // 1
+; float Source_multiplier; // 2
+; uint32 Target_stat; // 3
+; float Target_multiplier; // 4
+; };
+
+; // common struct for:
+; // ItemDamageAmmo.dbc
+; // ItemDamageOneHand.dbc
+; // ItemDamageOneHandCaster.dbc
+; // ItemDamageRanged.dbc
+; // ItemDamageThrown.dbc
+; // ItemDamageTwoHand.dbc
+; // ItemDamageTwoHandCaster.dbc
+; // ItemDamageWand.dbc
+; struct ItemDamageEntry
+; {
+; uint32 ID; // 0 item level
+; float Quality[7]; // 1-7 multiplier for item quality
+; uint32 ItemLevel; // 8 item level
+; };
+
+; struct ItemArmorQualityEntry
+; {
+; uint32 ID; // 0
+; float Qualitymod[7]; // 1-7 multiplier for item quality
+; uint32 ItemLevel; // 8 item level
+; };
+
+; struct ItemArmorShieldEntry
+; {
+; uint32 ID; // 0
+; uint32 ItemLevel; // 1 item level
+; float Quality[7]; // 2-8 multiplier for item quality
+; };
+
+; struct ItemArmorTotalEntry
+; {
+; uint32 ID; // 0
+; uint32 ItemLevel; // 1 item level
+; float Value[4]; // 2-5 multiplier for armor types (cloth...plate)
+; };
+
+; // ItemClass.dbc
+; struct ItemClassEntry
+; {
+; // uint32 ID; // 0
+; uint32 ClassID; // 1 item class id
+; //uint32 SubclassMapID; // 2
+; //uint32 Flags; // 3 1 for weapon, 0 for everything else
+; float PriceModifier; // 4 used to calculate certain prices
+; //char* ClassName; // 5 class name
+; };
+
+; struct ItemBagFamilyEntry
+; {
+; uint32 ID; // 0
+; //char* Name; // 1
+; };
+
+; struct ItemDisplayInfoEntry
+; {
+; uint32 ID; // 0
+; // char* ModelName[2]; // 1 - 2
+; // char* ModelTexture[2]; // 3 - 4
+; // char* InventoryIcon[2]; // 5 - 6
+; // uint32 GeosetGroup[2]; // 7 - 8
+; // uint32 Flags; // 9
+; // uint32 SpellVisualID; // 10
+; // uint32 GroupSoundIndex; // 11
+; // uint32 HelmetGeosetVisID[2]; // 12 - 13
+; // char* Texture[8] // 14 - 21
+; // uint32 ItemVisual; // 22
+; // uint32 ParticleColorID; // 23
+; };
+
+; struct ItemDisenchantLootEntry
+; {
+; uint32 ID; // 0
+; uint32 Class; // 1
+; int32 Subclass; // 2
+; uint32 Quality; // 3
+; uint32 MinLevel; // 4
+; uint32 MaxLevel; // 5
+; uint32 SkillRequired; // 6
+; };
+
+; struct ItemLimitCategoryEntry
+; {
+; uint32 ID; // 0
+; //char* Name; // 1
+; uint32 Quantity; // 2 max allowed equipped as item or in gem slot
+; uint32 Flags; // 3 0 = have, 1 = equip (enum ItemLimitCategoryMode)
+; };
+
+; struct ItemRandomPropertiesEntry
+; {
+; uint32 ID; // 0
+; //char* Name // 1
+; uint32 Enchantment[5]; // 2 - 6
+; char* Name; // 7
+; };
+
+; struct ItemRandomSuffixEntry
+; {
+; uint32 ID; // 0
+; char* Name; // 1
+; // char* InternalName // 2
+; uint32 Enchantment[5]; // 3 - 7
+; uint32 AllocationPct[5]; // 8 - 12
+; };
+
+; struct ItemSetEntry
+; {
+; //uint32 ID // 0
+; char* Name; // 1
+; uint32 ItemID[17]; // 2-18
+; uint32 SetSpellID[8]; // 19-26
+; uint32 SetThreshold[8]; // 27-34
+; uint32 RequiredSkill; // 35
+; uint32 RequiredSkillRank; // 36
+; };
+
+; struct LFGDungeonEntry
+; {
+; uint32 ID; // 0
+; char* Name; // 1
+; uint32 MinLevel; // 2
+; uint32 Maxlevel; // 3
+; uint32 Target_level; // 4
+; uint32 Target_level_min; // 5
+; uint32 Target_level_max; // 6
+; int32 MapID; // 7
+; uint32 DifficultyID; // 8
+; uint32 Flags; // 9
+; uint32 TypeID; // 10
+; //uint32 Faction; // 11
+; //char* TextureFilename; // 12
+; uint32 ExpansionLevel; // 13
+; //uint32 Order_index; // 14
+; uint32 Group_ID; // 15
+; //char* Description; // 16 Description
+; uint32 Random_ID; // 17 RandomDungeonID assigned for this dungeon
+; uint32 Count_tank; // 18
+; uint32 Count_healer; // 19
+; uint32 Count_damage; // 20
+; };
+
+; struct LFGDungeonsGroupingMapEntry
+; {
+; uint32 ID; // 0
+; uint32 LfgDungeonsID; // 1
+; uint32 Random_lfgDungeonsID; // 2
+; uint32 Group_ID; // 3
+; };
+
+; struct LightEntry
+; {
+; uint32 ID; // 0
+; uint32 ContinentID; // 1
+; float X; // 2
+; float Y; // 3
+; float Z; // 4
+; // float FalloffStart; // 5
+; // float FalloffEnd; // 6
+; // uint32 LightParamsID[8]; // 7 - 14
+; };
+
+; struct LiquidTypeEntry
+; {
+; uint32 ID; // 1
+; //char* Name; // 2
+; //uint32 Flags; // 3
+; uint32 SoundBank; // 4
+; //uint32 SoundID; // 5
+; uint32 SpellID; // 6
+; //float MaxDarkenDepth; // 7
+; //float FogDarkenIntensity; // 8
+; //float AmbDarkenIntensity; // 9
+; //float DirDarkenIntensity; // 10
+; //uint32 LightID; // 11
+; //float ParticleScale; // 12
+; //uint32 ParticleMovement; // 13
+; //uint32 ParticleTexSlots; // 14
+; //uint32 MaterialID; // 15
+; //char* Texture[6]; // 16 - 20
+; //uint32 Color[2]; // 21 - 22
+; //float Float[18]; // 23 - 40
+; //uint32 Int[4]; // 41 - 44
+; };
+
+; struct LockEntry
+; {
+; uint32 ID; // 0
+; uint32 Type[8]; // 1-8
+; uint32 Index[8]; // 9-16
+; uint32 Skill[8]; // 17-24
+; //uint32 Action[8]; // 25-32
+; };
+
+; struct PhaseEntry
+; {
+; uint32 ID; // 0
+; char* Name; // 1
+; uint32 Flags; // 2
+; };
+
+; struct PhaseGroupEntry
+; {
+; uint32 ID; // 1
+; uint32 PhaseID; // 2
+; uint32 PhaseGroupID; // 3
+; };
+
+; struct MailTemplateEntry
+; {
+; uint32 ID; // 0
+; //char* Subject; // 1
+; char* Body; // 2
+; };
+
+; struct MapEntry
+; {
+; uint32 ID; // 0
+; char const* Directory; // 1
+; uint32 MapType; // 2
+; uint32 Flags; // 3
+; uint32 InstanceType; // 4
+; uint32 PVP; // 5 0 or 1 for battlegrounds (not arenas)
+; char const* MapName; // 6
+; uint32 AreaTableID; // 7
+; char const* MapDescription0; // 8 Horde
+; char const* MapDescription1; // 9 Alliance
+; uint32 LoadingScreenID; // 10 (LoadingScreens.dbc)
+; float MinimapIconScale; // 11
+; int32 CorpseMapID; // 12 map_id of entrance map in ghost mode (continent always and in most cases = normal entrance)
+; DBCPosition2D Corpse; // 13 - 14 entrance coordinates in ghost mode (in most cases = normal entrance)
+; uint32 TimeOfDayOverride; // 15
+; uint32 ExpansionID; // 16
+; uint32 RaidOffset; // 17
+; uint32 MaxPlayers; // 18
+; int32 ParentMapID; // 19
+; };
+
+; struct MapDifficultyEntry
+; {
+; //uint32 ID; // 0
+; uint32 MapID; // 1
+; uint32 Difficulty; // 2 (for arenas: arena slot)
+; char* Message; // 3 (text showed when transfer to map failed)
+; uint32 RaidDuration; // 4 in secs, 0 if no fixed reset time
+; uint32 MaxPlayers; // 5 some heroic versions have 0 when expected same amount as in normal version
+; //char* Difficultystring; // 6
+; };
+
+; struct MountCapabilityEntry
+; {
+; uint32 ID; // 1
+; uint32 Flags; // 2
+; uint32 ReqRidingSkill; // 3
+; uint32 ReqAreaID; // 4
+; uint32 ReqSpellAuraID; // 5
+; uint32 ReqSpellKnownID; // 6
+; uint32 ModSpellAuraID; // 7
+; int32 ReqMapID; // 8
+; };
+
+; struct MountTypeEntry
+; {
+; uint32 ID;
+; uint32 Capability[24];
+; };
+
+; struct MovieEntry
+; {
+; uint32 ID; // 0 index
+; //char* Filename; // 1
+; //uint32 Volume; // 2
+; //uint32 KeyID; // 3
+; };
+
+; struct NameGenEntry
+; {
+; //uint32 ID; // 1
+; char* Name; // 2
+; uint32 RaceID; // 3
+; uint32 Sex; // 4
+; };
+
+; struct NumTalentsAtLevelEntry
+; {
+; //uint32 Level; // 0 index
+; float NumberOfTalents; // 1 talent count
+; };
+
+; struct NamesProfanityEntry
+; {
+; // uint32 ID; // 0
+; char const* Name; // 1
+; int32 Language; // 2
+; };
+
+; struct NamesReservedEntry
+; {
+; // uint32 ID; // 0
+; char const* Name; // 1
+; int32 Language; // 2
+; };
+
+; struct OverrideSpellDataEntry
+; {
+; uint32 ID; // 0
+; uint32 Spells[10]; // 1-10
+; // uint32 Flags; // 11
+; // char* PlayerActionbar; // 12
+; };
+
+; struct PlayerConditionEntry
+; {
+; uint32 ID; // 0
+; char* FailureDescription; // 1
+; };
+
+; struct PowerDisplayEntry
+; {
+; uint32 ID; // 0
+; uint32 ActualType; // 1
+; // char* GlobalStringBaseTag; // 2
+; // uint8 Red; // 3
+; // uint8 Green; // 4
+; // uint8 Blue; // 5
+; // uint8 Padding // 6
+; };
+
+; struct PvPDifficultyEntry
+; {
+; //uint32 ID; // 0
+; uint32 MapID; // 1
+; uint32 RangeIndex; // 2
+; uint32 MinLevel; // 3
+; uint32 MaxLevel; // 4
+; uint32 Difficulty; // 5
+; };
+
+; struct QuestSortEntry
+; {
+; uint32 ID; // 0
+; //char* SortName; // 1
+; };
+
+; struct QuestXPEntry
+; {
+; uint32 ID; // 0
+; uint32 Difficulty[10]; // 1 - 10
+; };
+
+; struct QuestFactionRewEntry
+; {
+; uint32 ID; // 0
+; int32 Difficulty[10]; // 1 - 11
+; };
+
+; struct QuestPOIBlobEntry
+; {
+; uint32 ID; // 0
+; uint32 NumPoints; // 1
+; uint32 MapID; // 2
+; uint32 WorldMapAreaID; // 3
+; };
+
+; struct QuestPOIPointEntry
+; {
+; uint32 ID; // 0
+; int32 X; // 1
+; int32 Y; // 2
+; uint32 QuestPOIBlobID; // 3
+; };
+
+; struct RandomPropertiesPointsEntry
+; {
+; //uint32 Id; // 0 hidden key
+; uint32 itemLevel; // 1
+; uint32 EpicPropertiesPoints[5]; // 2-6
+; uint32 RarePropertiesPoints[5]; // 7-11
+; uint32 UncommonPropertiesPoints[5]; // 12-16
+; };
+
+; // ResearchBranch.dbc
+; struct ResearchBranchEntry
+; {
+; uint32 ID; // 0
+; char* Name; // 1
+; uint32 ResearchFieldID; // 2
+; uint32 CurrencyID; // 3
+; char* Texture; // 4
+; uint32 ItemID; // 5
+; };
+
+; // ResearchField.dbc
+; struct ResearchFieldEntry
+; {
+; uint32 ID; // 0
+; char* Name; // 1
+; uint32 Slot; // 2
+; };
+
+; // ResearchProject.dbc
+; struct ResearchProjectEntry
+; {
+; uint32 ID; // 0
+; char* Name; // 1
+; char* Description; // 2
+; uint32 Rarity; // 3
+; uint32 ResearchBranchID; // 4
+; uint32 SpellID; // 5
+; uint32 NumSockets; // 6
+; char* Texture; // 7
+; uint32 RequiredWeight; // 8
+; };
+
+; // ResearchSite.dbc
+; struct ResearchSiteEntry
+; {
+; uint32 ID; // 1
+; uint32 MapID; // 2
+; uint32 QuestPOIBlobID; // 3
+; char* Name; // 4
+; char* AreaPOIIconEnum; // 5
+; };
+
+; struct ScalingStatDistributionEntry
+; {
+; uint32 ID; // 0
+; int32 StatID[10]; // 1 - 10
+; uint32 Bonus[10]; // 11 - 20
+; uint32 Minlevel; // 21
+; uint32 Maxlevel; // 22
+; };
+
+; struct ScalingStatValuesEntry
+; {
+; uint32 ID; // 0
+; uint32 Charlevel; // 1
+; uint32 dpsMod[6]; // 2 - 7 DPS mod for level
+; uint32 Spellpower; // 8 spell power for level
+; uint32 StatMultiplier[5]; // 9 - 13 Multiplier for ScalingStatDistribution
+; uint32 Armor[8][4]; // 14 - 46 Armor for level
+; uint32 CloakArmor; // 47 armor for cloak
+; };
+
+; struct SkillLineCategoryEntry
+; {
+; uint32 id; // 0
+; char* name; // 1
+; uint32 displayOrder; // 2
+; };
+
+; struct SkillLineEntry
+; {
+; uint32 ID; // 0
+; int32 CategoryID; // 1
+; char* DisplayName; // 3
+; //char* Description; // 4
+; uint32 SpellIconID; // 5
+; //char* AlternateVerb; // 6
+; uint32 CanLink; // 7 (prof. with recipe)
+; };
+
+; struct SkillLineAbilityEntry
+; {
+; uint32 ID; // 0
+; uint32 SkillLine; // 1
+; uint32 Spell; // 2
+; uint32 RaceMask; // 3
+; uint32 ClassMask; // 4
+; //uint32 ExcludeRace; // 5
+; //uint32 ExcludeClass; // 6
+; uint32 MinSkillLineRank; // 7
+; uint32 SupercededBySpell; // 8
+; uint32 AcquireMethod; // 9
+; uint32 TrivialSkillLineRankHigh; // 10
+; uint32 TrivialSkillLineRankLow; // 11
+; uint32 NumSkillUps; // 12
+; uint32 UniqueBit; // 13
+; };
+
+; struct SkillRaceClassInfoEntry
+; {
+; //uint32 ID; // 0
+; uint32 SkillID; // 1
+; uint32 RaceMask; // 2
+; uint32 ClassMask; // 3
+; uint32 Flags; // 4
+; uint32 Availability; // 5
+; //uint32 MinLevel; // 6
+; uint32 SkillTierID; // 7
+; //uint32 SkillCostIndex; // 8
+; };
+
+; struct SkillTiersEntry
+; {
+; uint32 ID; // 0
+; //uint32 Cost[16]; // 1-16
+; uint32 Value[16]; // 17-32
+; };
+
+; struct SoundEntriesEntry
+; {
+; uint32 ID; // 0
+; //uint32 SoundType; // 1
+; //char* Name; // 2
+; //char* File[10]; // 3 - 12
+; //uint32 Freq[10]; // 13 - 22
+; //char* DirectoryBase; // 23
+; //float VolumeFloat; // 24
+; //uint32 Flags; // 25
+; //float MinDistance; // 26
+; //float DistanceCutoff; // 27
+; //uint32 EAXDef; // 28
+; //uint32 SoundEntriesAdvancedID; // 29
+; //float Volumevariationplus; // 30
+; //float Volumevariationminus; // 31
+; //float Pitchvariationplus; // 32
+; //float Pitchvariationminus; // 33
+; //float PitchAdjust; // 34
+; };
+
+; // SpellEffect.dbc
+; struct SpellEffectEntry
+; {
+; uint32 ID; // 0
+; uint32 Effect; // 1
+; float EffectAmplitude; // 2
+; uint32 EffectAura; // 3
+; uint32 EffectAuraPeriod; // 4
+; int32 EffectBasePoints; // 5
+; float EffectBonusCoefficient; // 6
+; float EffectChainAmplitude; // 7
+; uint32 EffectChainTargets; // 8
+; int32 EffectDieSides; // 9
+; uint32 EffectItemType; // 10
+; uint32 EffectMechanic; // 11
+; int32 EffectMiscValue; // 12
+; int32 EffectMiscValueB; // 13
+; float EffectPointsPerResource; // 14
+; uint32 EffectRadiusIndex; // 15
+; uint32 EffectRadiusMaxIndex; // 16
+; float EffectRealPointsPerLevel; // 17
+; flag96 EffectSpellClassMask; // 18 - 20
+; uint32 EffectTriggerSpell; // 21
+; uint32 EffectImplicitTargetA; // 22
+; uint32 EffectImplicitTargetB; // 23
+; uint32 SpellID; // 24
+; uint32 EffectIndex; // 25
+; //uint32 EffectAttributes // 26
+; };
+
+; // SpellAuraOptions.dbc
+; struct SpellAuraOptionsEntry
+; {
+; uint32 ID; // 0
+; uint32 CumulativeAura; // 1
+; uint32 ProcChance; // 2
+; uint32 ProcCharges; // 3
+; uint32 ProcTypeMask; // 4
+; };
+
+; // SpellAuraRestrictions.dbc/
+; struct SpellAuraRestrictionsEntry
+; {
+; //uint32 ID; // 0
+; uint32 CasterAuraState; // 1
+; uint32 TargetAuraState; // 2
+; uint32 ExcludeCasterAuraState; // 3
+; uint32 ExcludeTargetAuraState; // 4
+; uint32 CasterAuraSpell; // 5
+; uint32 TargetAuraSpell; // 6
+; uint32 ExcludeCasterAuraSpell; // 7
+; uint32 ExcludeTargetAuraSpell; // 8
+; };
+
+; // SpellCastingRequirements.dbc
+; struct SpellCastingRequirementsEntry
+; {
+; uint32 ID; // 0
+; uint32 FacingCasterFlags; // 1
+; uint32 MinFactionID; // 2
+; uint32 MinReputation; // 3
+; int32 RequiredAreasID; // 4
+; uint32 RequiredAuraVision; // 5
+; uint32 RequiresSpellFocus; // 6
+; };
+
+; // SpellTotems.dbc
+; struct SpellTotemsEntry
+; {
+; uint32 ID; // 0
+; uint32 RequiredTotemCategoryID[2]; // 1
+; uint32 Totem[2]; // 2
+; };
+
+; // Spell.dbc
+; struct SpellEntry
+; {
+; uint32 ID; // 0
+; uint32 Attributes; // 1
+; uint32 AttributesEx; // 2
+; uint32 AttributesEx2; // 3
+; uint32 AttributesEx3; // 4
+; uint32 AttributesEx4; // 5
+; uint32 AttributesEx5; // 6
+; uint32 AttributesEx6; // 7
+; uint32 AttributesEx7; // 8
+; uint32 AttributesEx8; // 9
+; uint32 AttributesEx9; // 1
+; uint32 AttributesEx10; // 1
+; uint32 CastingTimeIndex; // 1
+; uint32 DurationIndex; // 1
+; uint32 PowerType; // 1
+; uint32 RangeIndex; // 1
+; float Speed; // 1
+; uint32 SpellVisualID[2]; // 17 - 18
+; uint32 SpellIconID; // 19
+; uint32 ActiveIconID; // 20
+; char* Name; // 21
+; char* NameSubtext; // 22
+; //char* Description; // 23
+; //char* AuraDescription; // 24
+; uint32 SchoolMask; // 25
+; uint32 RuneCostID; // 26
+; //uint32 SpellMissileID; // 27
+; //uint32 DescriptionVariablesID; // 28
+; uint32 Difficulty; // 29
+; float BonusCoefficient; // 30
+; uint32 ScalingID; // 31
+; uint32 AuraOptionsID; // 32
+; uint32 AuraRestrictionsID; // 33
+; uint32 CastingRequirementsID; // 34
+; uint32 CategoriesID; // 35
+; uint32 ClassOptionsID; // 36
+; uint32 CooldownsID; // 37
+; //uint32 unkIndex7; // 38
+; uint32 EquippedItemsID; // 39
+; uint32 InterruptsID; // 40
+; uint32 LevelsID; // 41
+; uint32 PowerDisplayID; // 42
+; uint32 ReagentsID; // 43
+; uint32 ShapeshiftID; // 44
+; uint32 TargetRestrictionsID; // 45
+; uint32 TotemsID; // 46
+; uint32 RequiredProjectID; // 47
+; };
+
+; // SpellCategories.dbc
+; struct SpellCategoriesEntry
+; {
+; //uint32 ID; // 0
+; uint32 Category; // 1
+; uint32 DefenseType; // 2
+; uint32 DispelType; // 3
+; uint32 Mechanic; // 4
+; uint32 PreventionType; // 5
+; uint32 StartRecoveryCategory; // 6
+; };
+
+; struct SpellCastTimesEntry
+; {
+; uint32 ID; // 0
+; int32 Base; // 1
+; //int32 PerLevel; // 2
+; //int32 Minimum; // 3
+; };
+
+; struct SpellCategoryEntry
+; {
+; uint32 ID; // 0
+; uint32 Flags; // 1
+; uint32 UsesPerWeek; // 2
+; // char* Name; // 3
+; };
+
+; struct SpellDifficultyEntry
+; {
+; uint32 ID; // 0
+; int32 DifficultySpellID[4]; // 1 - 4 instance modes: 10N, 25N, 10H, 25H or Normal/Heroic if only 1-2 is set, if 3-4 is 0 then Mode-2
+; };
+
+; struct SpellFocusObjectEntry
+; {
+; uint32 ID; // 0
+; //char* Name; // 1
+; };
+
+; struct SpellRadiusEntry
+; {
+; uint32 ID; // 0
+; float RadiusMin; // 1
+; float RadiusPerLevel; // 2
+; float RadiusMax; // 3
+; };
+
+; struct SpellRangeEntry
+; {
+; uint32 ID; // 1
+; float RangeMin[2]; // 2 - 3
+; float RangeMax[2]; // 4 - 5
+; uint32 Flags; // 6
+; //char* DisplayName; // 7
+; //char* DisplayNameShort; // 8
+; };
+
+; // SpellEquippedItems.dbc
+; struct SpellEquippedItemsEntry
+; {
+; //uint32 ID; // 1
+; int32 EquippedItemClass; // 2
+; int32 EquippedItemInvTypes; // 3
+; int32 EquippedItemSubclass; // 4
+; };
+
+; // SpellCooldowns.dbc
+; struct SpellCooldownsEntry
+; {
+; //uint32 ID; // 0
+; uint32 CategoryRecoveryTime; // 1
+; uint32 RecoveryTime; // 2
+; uint32 StartRecoveryTime; // 3
+; };
+
+; // SpellClassOptions.dbc
+; struct SpellClassOptionsEntry
+; {
+; //uint32 ID; // 0
+; //uint32 ModalNextSpell; // 1
+; flag96 SpellFamilyMask; // 2-4
+; uint32 SpellClassSet; // 5
+; //char* Description; // 6
+; };
+
+; // SpellInterrupts.dbc
+; struct SpellInterruptsEntry
+; {
+; //uint32 ID; // 0
+; uint32 AuraInterruptFlags[2]; // 1 - 2
+; uint32 ChannelInterruptFlags[2]; // 3 - 4
+; uint32 InterruptFlags; // 5
+; };
+
+; // SpellLevels.dbc
+; struct SpellLevelsEntry
+; {
+; //uint32 ID; // 0
+; uint32 BaseLevel; // 1
+; uint32 MaxLevel; // 2
+; uint32 SpellLevel; // 3
+; };
+
+; // SpellPower.dbc
+; struct SpellPowerEntry
+; {
+; //uint32 ID; // 0
+; uint32 ManaCost; // 1
+; uint32 ManaCostPerLevel; // 2
+; uint32 PowerCostPct; // 3
+; uint32 ManaPerSecond; // 4
+; //uint32 PowerDisplayID; // 5
+; //uint32 AltPowerBarID; // 6
+; float PowerCostPct2; // 7
+; };
+
+; struct SpellRuneCostEntry
+; {
+; uint32 ID; // 0
+; uint32 RuneCost[3]; // 1 - 3 (0 = blood, 1 = frost, 2 = unholy)
+; uint32 RunicPower; // 4
+; };
+
+; struct SpellShapeshiftFormEntry
+; {
+; uint32 ID; // 0
+; //uint32 BonusActionBar; // 1 unused
+; //char* Name; // 2 unused
+; uint32 Flags; // 3
+; int32 CreatureType; // 4 <= 0 humanoid, other normal creature types
+; //uint32 AttackIconID; // 5 unused, related to next field
+; uint32 CombatRoundTime; // 6
+; uint32 CreatureDisplayID[4]; // 7 - 10
+; uint32 PresetSpellID[8]; // 11 - 18 spells which appear in the bar after shapeshifting
+; uint32 MountTypeID; // 19
+; //uint32 ExitSoundEntriesID; // 20
+; };
+
+; // SpellShapeshift.dbc
+; struct SpellShapeshiftEntry
+; {
+; uint32 ID; // 0
+; uint32 ShapeshiftExclude[2]; // 1
+; uint32 ShapeshiftMask[2]; // 3
+; // uint32 StanceBarOrder; // 5
+; };
+
+; // SpellTargetRestrictions.dbc
+; struct SpellTargetRestrictionsEntry
+; {
+; uint32 ID; // 0
+; float ConeAngle; // 1
+; uint32 MaxTargets; // 2
+; uint32 MaxTargetLevel; // 3
+; uint32 TargetCreatureType; // 4
+; uint32 Targets; // 5
+; };
+
+; // SpellReagents.dbc
+; struct SpellReagentsEntry
+; {
+; //uint32 ID; // 0
+; int32 Reagents[8]; // 1 - 8
+; uint32 ReagentCount[8]; // 9 - 16
+; };
+
+; // SpellScaling.dbc
+; struct SpellScalingEntry
+; {
+; //uint32 ID; // 0 m_ID
+; int32 CastTimeMin; // 1
+; int32 CastTimeMax; // 2
+; int32 CastTimeMaxLevel; // 3
+; int32 Class; // 4 (index * 100) + charLevel - 1 => gtSpellScaling.dbc
+; float Coefficient[3]; // 5 - 7
+; float Variance[3]; // 8 - 10
+; float ComboPointsCoefficient[3]; // 11 - 13
+; float NerfFactor; // 14 some coefficient, mostly 1.0f
+; int32 NerfMaxLevel; // 15 some level
+; };
+
+; struct SpellDurationEntry
+; {
+; uint32 ID; // 0
+; int32 Duration; // 1
+; int32 DurationPerLevel; // 2
+; int32 MaxDuration; // 3
+; };
+
+; struct SpellItemEnchantmentEntry
+; {
+; uint32 ID; // 0
+; //uint32 Charges; // 1
+; uint32 Effect[3]; // 2 - 4
+; uint32 EffectPointsMin[3]; // 5 - 7
+; //uint32 EffectPointsMax[3];// 8 - 10
+; uint32 EffectArg[3]; // 11 - 13
+; char* Name; // 14
+; uint32 ItemVisual; // 15
+; uint32 Flags; // 16
+; uint32 Src_itemID; // 17
+; uint32 Condition_ID; // 18
+; uint32 RequiredSkillID; // 19
+; uint32 RequiredSkillRank; // 20
+; uint32 MinLevel; // 21
+; //uint32 ItemLevel; // 22
+; };
+
+; struct SpellItemEnchantmentConditionEntry
+; {
+; uint32 ID; // 0 m_ID
+; uint8 Color[3]; // 1-3 m_lt_operandType[5]
+; //uint8 unk1; // 4
+; //uint32 unk2[6]; // 5-10
+; uint8 Comparator[3]; // 11-13 m_operator[5]
+; //uint8 unk3[2]; // 14-15
+; uint8 CompareColor[3]; // 16-18 m_rt_operandType[5]
+; //uint32 unk4; // 19
+; uint32 Value[3]; // 20-22 m_rt_operand[5]
+; //uint32 unk5[2]; // 23-24
+; //uint8 unk6[6]; // 25-30
+; };
+
+; struct SpellVisualEntry
+; {
+; //uint32 ID;
+; //uint32 PrecastKit;
+; //uint32 CastKit;
+; //uint32 ImpactKit;
+; //uint32 StateKit;
+; //uint32 StateDoneKit;
+; //uint32 ChannelKit;
+; uint32 HasMissile;
+; int32 MissileModel;
+; //uint32 MissilePathType;
+; //uint32 MissileDestinationAttachment;
+; //uint32 MissileSound;
+; //uint32 AnimEventSoundID;
+; //uint32 Flags;
+; //uint32 CasterImpactKit;
+; //uint32 TargetImpactKit;
+; //int32 MissileAttachment;
+; //uint32 MissileFollowGroundHeight;
+; //uint32 MissileFollowGroundDropSpeed;
+; //uint32 MissileFollowGroundApprach;
+; //uint32 MissileFollowGroundFlags;
+; //uint32 MissileMotionId;
+; //uint32 MissileTargetingKit;
+; //uint32 InstantAreaKit;
+; //uint32 ImpactAreaKit;
+; //uint32 PersistentAreaKit;
+; //DBCPosition3D MissileCastOffset;
+; //DBCPosition3D MissileImpactOffset;
+; uint32 AlternativeVisualID;
+; };
+
+; struct SpellVisualKitEntry
+; {
+; uint32 ID;
+; uint32 StartAnimID;
+; uint32 AnimID;
+; uint32 AnimKitID;
+; uint32 HeadEffect;
+; uint32 ChestEffect;
+; uint32 BaseEffect;
+; uint32 LeftHandEffect;
+; uint32 RightHandEffect;
+; uint32 BreathEffect;
+; uint32 LeftWeaponEffect;
+; uint32 RightWeaponEffect;
+; uint32 SpecialEffect[3];
+; uint32 WorldEffect;
+; uint32 SoundID;
+; uint32 ShakeID;
+; uint32 CharProc[4];
+; uint32 CharParamZero[4];
+; uint32 CharParamOne[4];
+; uint32 CharParamTwo[4];
+; uint32 CharParamThree[4];
+; uint32 Flags;
+; };
+
+; struct SummonPropertiesEntry
+; {
+; uint32 ID; // 0
+; uint32 Control; // 1, 0 - can't be controlled?, 1 - something guardian?, 2 - pet?, 3 - something controllable?, 4 - taxi/mount?
+; uint32 Faction; // 2, 14 rows > 0
+; int32 Title; // 3, see enum
+; int32 Slot; // 4, 0-6
+; uint32 Flags; // 5
+; };
+
+; struct TalentEntry
+; {
+; uint32 ID; // 0
+; uint32 TabID; // 1 index in TalentTab.dbc (TalentTabEntry)
+; uint32 TierID; // 2
+; uint32 ColumnIndex; // 3
+; uint32 SpellRank[MAX_TALENT_RANK]; // 4-8
+; uint32 PrereqTalent[3]; // 9 - 11 (Talent.dbc)
+; uint32 PrereqRank[3]; // 12 - 14 part of prev field
+; //uint32 Flags; // 15 also need disable higest ranks on reset talent tree
+; //uint32 RequiredSpellID; // 16
+; //uint64 CategoryMask[2]; // 17 - 18 its a 64 bit mask for pet 1 << m_categoryEnumID in CreatureFamily.dbc
+; };
+
+; struct TalentTabEntry
+; {
+; uint32 ID; // 0
+; //char* Name; // 1
+; //unit32 SpellIconID; // 2
+; uint32 ClassMask; // 3
+; uint32 CategoryEnumID; // 4
+; uint32 OrderIndex; // 5
+; //char* BackgroundFile; // 6
+; //char* Description; // 7
+; //uint32 RoleMask; // 8
+; uint32 MasterySpellID[2]; // 9 - 10 passive mastery bonus spells
+; };
+
+; struct TalentTreePrimarySpellsEntry
+; {
+; //uint32 ID; // 0 index
+; uint32 TalentTabID; // 1 entry from TalentTab.dbc
+; uint32 SpellID; // 2 spell id to learn
+; //uint32 Flags; // 3 some kind of flags
+; };
+
+; struct TaxiNodesEntry
+; {
+; uint32 ID; // 0
+; uint32 ContinentID; // 1
+; DBCPosition3D Pos; // 2 - 4
+; char* Name; // 5
+; uint32 MountCreatureID[2]; // 6 - 7
+; uint32 Flags; // 8
+; DBCPosition2D MapOffset; // 9 - 10
+; };
+
+; struct TaxiPathEntry
+; {
+; uint32 ID; // 0
+; uint32 FromTaxiNode; // 1
+; uint32 ToTaxiNode; // 2
+; uint32 Cost; // 3
+; };
+
+; struct TaxiPathNodeEntry
+; {
+; //uint32 ID; // 0
+; uint32 PathID; // 1
+; uint32 NodeIndex; // 2
+; uint32 ContinentID; // 3
+; DBCPosition3D Loc; // 4 - 6
+; uint32 Flags; // 7
+; uint32 Delay; // 8
+; uint32 ArrivalEventID; // 9
+; uint32 DepartureEventID; // 10
+; };
+
+; struct TotemCategoryEntry
+; {
+; uint32 ID; // 0
+; //char* Name; // 1
+; uint32 TotemCategoryType; // 2 (one for specialization)
+; uint32 TotemCategoryMask; // 3 (compatibility mask for same type: different for totems, compatible from high to low for rods)
+; };
+
+; struct UnitPowerBarEntry
+; {
+; uint32 ID; // 1
+; uint32 MinPower; // 2
+; uint32 MaxPower; // 3
+; uint32 StartPower; // 4
+; //uint32 CenterPower; // 5
+; float RegenerationPeace; // 6
+; float RegenerationCombat; // 7
+; //uint32 BarType; // 8
+; //uint32 FileDataID[6]; // 9 - 14
+; //uint32 Color[6]; // 15 - 20
+; //uint32 Flags; // 21
+; //char* Name; // 22
+; //char* Cost; // 23
+; //char* OutOfError; // 24
+; //char* ToolTip; // 25
+; //float StartInset; // 26
+; //float EndInset; // 27
+; };
+
+; struct TransportAnimationEntry
+; {
+; //uint32 ID; // 1
+; uint32 TransportID; // 2
+; uint32 TimeIndex; // 3
+; DBCPosition3D Pos; // 4 - 6
+; //uint32 SequenceID; // 7
+; };
+
+; struct TransportRotationEntry
+; {
+; //uint32 ID; // 1
+; uint32 GameObjectsID; // 2
+; uint32 TimeIndex; // 3
+; float X; // 4
+; float Y; // 5
+; float Z; // 6
+; float W; // 7
+; };
+
+; struct VehicleEntry
+; {
+; uint32 ID; // 0
+; uint32 Flags; // 1
+; float TurnSpeed; // 2
+; float PitchSpeed; // 3
+; float PitchMin; // 4
+; float PitchMax; // 5
+; uint32 SeatID[8]; // 6 - 13
+; float MouseLookOffsetPitch; // 14
+; float CameraFadeDistScalarMin; // 15
+; float CameraFadeDistScalarMax; // 16
+; float CameraPitchOffset; // 17
+; float FacingLimitRight; // 18
+; float FacingLimitLeft; // 19
+; float MsslTrgtTurnLingering; // 20
+; float MsslTrgtPitchLingering; // 21
+; float MsslTrgtMouseLingering; // 22
+; float MsslTrgtEndOpacity; // 23
+; float MsslTrgtArcSpeed; // 24
+; float MsslTrgtArcRepeat; // 25
+; float MsslTrgtArcWidth; // 26
+; float MsslTrgtImpactRadius[2]; // 27 - 28
+; char* MsslTrgtArcTexture; // 29
+; char* MsslTrgtImpactTexture; // 30
+; char* MsslTrgtImpactModel[2]; // 31 - 32
+; float CameraYawOffset; // 33
+; uint32 UiLocomotionType; // 34
+; float MsslTrgtImpactTexRadius; // 35
+; uint32 VehicleUIIndicatorID; // 36
+; uint32 PowerDisplayID[3]; // 37
+; };
+
+; struct VehicleSeatEntry
+; {
+; uint32 ID; // 0
+; uint32 Flags; // 1
+; int32 AttachmentID; // 2
+; DBCPosition3D AttachmentOffset; // 3 - 5
+; float EnterPreDelay; // 6
+; float EnterSpeed; // 7
+; float EnterGravity; // 8
+; float EnterMinDuration; // 9
+; float EnterMaxDuration; // 10
+; float EnterMinArcHeight; // 11
+; float EnterMaxArcHeight; // 12
+; int32 EnterAnimStart; // 13
+; int32 EnterAnimLoop; // 14
+; int32 RideAnimStart; // 15
+; int32 RideAnimLoop; // 16
+; int32 RideUpperAnimStart; // 17
+; int32 RideUpperAnimLoop; // 18
+; float ExitPreDelay; // 19
+; float ExitSpeed; // 20
+; float ExitGravity; // 21
+; float ExitMinDuration; // 22
+; float ExitMaxDuration; // 23
+; float ExitMinArcHeight; // 24
+; float ExitMaxArcHeight; // 25
+; int32 ExitAnimStart; // 26
+; int32 ExitAnimLoop; // 27
+; int32 ExitAnimEnd; // 28
+; float PassengerYaw; // 29
+; float PassengerPitch; // 30
+; float PassengerRoll; // 31
+; int32 PassengerAttachmentID; // 32
+; int32 VehicleEnterAnim; // 33
+; int32 VehicleExitAnim; // 34
+; int32 VehicleRideAnimLoop; // 35
+; int32 VehicleEnterAnimBone; // 36
+; int32 VehicleExitAnimBone; // 37
+; int32 VehicleRideAnimLoopBone; // 38
+; float VehicleEnterAnimDelay; // 39
+; float VehicleExitAnimDelay; // 40
+; uint32 VehicleAbilityDisplay; // 41
+; uint32 EnterUISoundID; // 42
+; uint32 ExitUISoundID; // 43
+; int32 UISkin; // 44
+; uint32 FlagsB; // 45
+; float CameraEnteringDelay; // 46
+; float CameraEnteringDuration; // 47
+; float CameraExitingDelay; // 48
+; float CameraExitingDuration; // 49
+; DBCPosition3D CameraOffset; // 50 - 52
+; float CameraPosChaseRate; // 53
+; float CameraFacingChaseRate; // 54
+; float CameraEnteringZoom; // 55
+; float CameraSeatZoomMin; // 56
+; float CameraSeatZoomMax; // 57
+; uint32 EnterAnimKitID; // 58
+; uint32 RideAnimKitID; // 59
+; uint32 ExitAnimKitID; // 60
+; uint32 VehicleEnterAnimKitID; // 61
+; uint32 VehicleRideAnimKitID; // 62
+; uint32 VehicleExitAnimKitID; // 63
+; uint32 CameraModeID; // 64
+; uint32 FlagsC; // 65
+; };
+
+; struct WMOAreaTableEntry
+; {
+; uint32 ID; // 0 index
+; int32 WMOID; // 1 used in root WMO
+; int32 NameSetID; // 2 used in adt file
+; int32 WMOGroupID; // 3 used in group WMO
+; //uint32 SoundProviderPref; // 4
+; //uint32 SoundProviderPrefUnderwater; // 5
+; //uint32 AmbienceID; // 6
+; //uint32 ZoneMusic; // 7
+; //uint32 IntroSound; // 8
+; uint32 Flags; // 9 used for indoor/outdoor determination
+; uint32 AreaTableID; // 10 link to AreaTableEntry.ID
+; //char* AreaName; // 11
+; //uint32 UwIntroSound; // 12
+; //uint32 UwZoneMusic; // 13
+; //uint32 UwAmbience; // 14
+; };
+
+; struct WorldSafeLocsEntry
+; {
+; uint32 ID; // 0
+; uint32 Continent; // 1
+; DBCPosition3D Loc; // 2 - 4
+; //char* AreaName; // 5
+; };
+
+; struct WorldStateSounds
+; {
+; uint32 ID; // 0 Worldstate
+; uint32 unk; // 1
+; uint32 areaTable; // 2
+; uint32 WMOAreaTable; // 3
+; uint32 zoneIntroMusicTable; // 4
+; uint32 zoneIntroMusic; // 5
+; uint32 zoneMusic; // 6
+; uint32 soundAmbience; // 7
+; uint32 soundProviderPreferences; // 8
+; };
+
+; struct WorldStateUI
+; {
+; uint32 ID; // 0
+; uint32 map_id; // 1 Can be -1 to show up everywhere.
+; uint32 zone; // 2 Can be zero for "everywhere".
+; uint32 phaseMask; // 3 Phase this WorldState is avaliable in
+; uint32 icon; // 4 The icon that is used in the interface.
+; char* textureFilename; // 5
+; char* text; // 6-21 The worldstate text
+; char* description; // 22-38 Text shown when hovering mouse on icon
+; uint32 worldstateID; // 39 This is the actual ID used
+; uint32 type; // 40 0 = unknown, 1 = unknown, 2 = not shown in ui, 3 = wintergrasp
+; uint32 unk1; // 41
+; uint32 unk2; // 43
+; uint32 unk3; // 44-58
+; uint32 unk4; // 59-61 Used for some progress bars.
+; uint32 unk7; // 62 Unused in 3.3.5a
+; };
diff --git a/setup/tools/dbcreader.class.php b/setup/tools/dbcreader.class.php
new file mode 100644
index 00000000..3138f623
--- /dev/null
+++ b/setup/tools/dbcreader.class.php
@@ -0,0 +1,362 @@
+
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see .
+*/
+
+if (!defined('AOWOW_REVISION'))
+ die('illegal access');
+
+if (!CLI)
+ die('not in cli mode');
+
+
+class DBCReader
+{
+ private const /* string */ INI_FILE_PATH = 'setup/tools/dbc/%s.ini';
+ private const /* int */ MAX_INSERT_ROWS = 500;
+
+ public const /* string */ DEFAULT_WOW_BUILD = '12340';
+
+ private bool $isGameTable = false;
+ private bool $isLocalized = false;
+ private bool $isTempTable = true;
+ private string $tableName = '';
+ private array $dataBuffer = [];
+ private array $fileRefs = [];
+ private array $format = [];
+ private string $recordFmt = '';
+ private array $macro = array(
+ 'LOC' => 'sxsssxsxsxxxxxxxx', // pre 4.x locale block (in use)
+ 'X_LOC' => 'xxxxxxxxxxxxxxxxx' // pre 4.x locale block (unused)
+ );
+ private array $unpackFmt = array( // Supported format characters:
+ 'x' => Primitive::PACK_FMT.'4', // x - not used/unknown, 4 bytes
+ 'X' => Primitive::PACK_FMT, // X - not used/unknown, 1 byte
+ 's' => UInt32::PACK_FMT, // s - string block index, 4 bytes
+ 'S' => UInt32::PACK_FMT, // S - string block index, 4 bytes - localized; autofill
+ 'f' => Double::PACK_FMT, // f - float, 4 bytes (rounded to 4 digits after comma)
+ 'i' => Int32::PACK_FMT, // i - signed int, 4 bytes
+ 'I' => Int32::PACK_FMT, // I - signed int, 4 bytes, sql index
+ 'u' => UInt32::PACK_FMT, // u - unsigned int, 4 bytes
+ 'U' => UInt32::PACK_FMT, // U - unsigned int, 4 bytes, sql index
+ 'b' => UInt8::PACK_FMT, // b - unsigned char, 1 byte
+ 'd' => Primitive::PACK_FMT.'4', // d - ordered by this field, not included in array
+ 'n' => UInt32::PACK_FMT // n - unsigned int, 4 bytes, sql primary key
+ );
+
+ private static array $structs = [];
+
+ public bool $error = true;
+
+ public function __construct(public string $file, array $opts = [], string $wowBuild = self::DEFAULT_WOW_BUILD)
+ {
+ self::loadStructs($wowBuild);
+
+ $this->file = strtolower($this->file);
+ if (empty(self::$structs[$this->file]))
+ {
+ CLI::write('no structure known for '.$this->file.'.dbc, build '.$wowBuild, CLI::LOG_ERROR);
+ return;
+ }
+
+ foreach (self::$structs[$this->file] as $name => $type)
+ {
+ // resolove locale macro
+ if (isset($this->macro[$type]))
+ {
+ $this->isLocalized = true;
+ for ($i = 0; $i < strlen($this->macro[$type]); $i++)
+ {
+ $this->format[$name.'_loc'.$i] = $this->macro[$type][$i];
+ $this->recordFmt .= '/'.$this->unpackFmt[$this->macro[$type][$i]].$name.'_loc'.$i;
+ }
+ }
+ else if (!isset($this->unpackFmt[$type]))
+ {
+ CLI::write('unknown format parameter '.CLI::bold($type).' at for field '.CLI::bold($name).' in format string', CLI::LOG_ERROR);
+ return;
+ }
+ else
+ {
+ $this->format[$name] = $type;
+ $this->recordFmt .= '/'.$this->unpackFmt[$type];
+ if ($type !== 'x' && $type !== 'X')
+ $this->recordFmt .= $name;
+
+ if ($type === 'S')
+ $this->isLocalized = true;
+ }
+ }
+
+ // Optimizing unpack string: 'x/x/x/x/x/x' => 'x6'
+ $this->recordFmt = preg_replace_callback('/x(\/x)+/i', fn($m) => 'x'.((strlen($m[0]) + 1) / 2), substr($this->recordFmt, 1));
+
+ if (is_bool($opts['temporary']))
+ $this->isTempTable = $opts['temporary'];
+
+ if (!empty($opts['tableName']))
+ $this->tableName = $opts['tableName'];
+ else
+ $this->tableName = 'dbc_'.$this->file;
+
+ // gameTable-DBCs don't have an index and are accessed through value order
+ // allas, you cannot do this with mysql, so we add a 'virtual' index
+ $this->isGameTable = array_values($this->format) == ['f'] && substr($this->file, 0, 2) == 'gt';
+
+ $foundMask = 0x0;
+ foreach (Locale::cases() as $loc)
+ {
+ if (!in_array($loc, CLISetup::$locales))
+ continue;
+
+ if ($foundMask & (1 << $loc->value))
+ continue;
+
+ foreach ($loc->gameDirs() as $dir)
+ {
+ $fullPath = CLI::nicePath($this->file.'.dbc', CLISetup::$srcDir, $dir, 'DBFilesClient');
+ if (!CLISetup::fileExists($fullPath))
+ continue;
+
+ $dbcFile = new DBCFile($fullPath);
+ if ($dbcFile->error)
+ {
+ CLI::write($dbcFile->error, CLI::LOG_ERROR);
+ unset($dbcFile);
+ continue;
+ }
+
+ if ($dbcFile->nCols != count($this->format))
+ {
+ CLI::write('incorrect format specified for file '.$this->file.' - expected fields: '.count($this->format).' read fields: '.$dbcFile->nCols, CLI::LOG_ERROR);
+ unset($dbcFile);
+ continue;
+ }
+
+ $recSize = 0;
+ foreach ($this->format as $ch)
+ $recSize += ($ch == 'X' || $ch == 'b') ? 1 : 4;
+
+ if ($recSize != $dbcFile->recordSize)
+ {
+ CLI::write('format string size ('.$recSize.') for file '.$this->file.' does not match actual size ('.$dbcFile->recordSize.')', CLI::LOG_ERROR);
+ unset($dbcFile);
+ continue;
+ }
+
+ $this->fileRefs[$loc->value] = $dbcFile;
+ $foundMask |= (1 << $loc->value);
+ }
+ }
+
+ if (!$this->fileRefs)
+ {
+ CLI::write('no suitable files found for '.$this->file.'.dbc, aborting.', CLI::LOG_ERROR);
+ return;
+ }
+
+ // check if DBCs are identical
+
+ $tests = ['nRows' => null, 'nCols' => null, 'recordSize' => null];
+ foreach ($this->fileRefs as $fileRef)
+ {
+ foreach ($tests as $field => $val)
+ {
+ if ($val === null)
+ $tests[$field] = $fileRef->{$field};
+ else if ($val != $fileRef->{$field})
+ {
+ CLI::write('some DBCs have different '.$field.': '.CLI::bold($val).' <> '.CLI::bold($fileRef->{$field}).' respectively. cannot merge!', CLI::LOG_ERROR);
+ return;
+ }
+ }
+ }
+
+ $this->error = false;
+ }
+
+ public function readFile() : bool
+ {
+ if (!$this->file || $this->error)
+ return false;
+
+ $this->createTable();
+
+ if ($this->isLocalized)
+ CLI::write(' - DBC: reading and merging '.$this->file.'.dbc for locales '.Lang::concat(array_keys($this->fileRefs), callback: fn($x) => CLI::bold(Locale::from($x)->name)));
+ else
+ CLI::write(' - DBC: reading '.$this->file.'.dbc');
+
+ $this->read();
+
+ return true;
+ }
+
+ public function getTableName() : string
+ {
+ return $this->tableName;
+ }
+
+ public static function getDefinitions() : array
+ {
+ if (empty(self::$structs))
+ self::loadStructs();
+
+ return array_keys(self::$structs);
+ }
+
+ private static function loadStructs(string $wowBuild = self::DEFAULT_WOW_BUILD) : void
+ {
+ $structFile = sprintf(self::INI_FILE_PATH, $wowBuild);
+
+ if (!file_exists($structFile))
+ {
+ CLI::write('no structure file found for wow build '.$wowBuild, CLI::LOG_ERROR);
+ return;
+ }
+
+ self::$structs = parse_ini_file($structFile, true);
+ }
+
+ private function endClean() : void
+ {
+ unset($this->fileRefs, $this->dataBuffer);
+ }
+
+ private function createTable() : void
+ {
+ if ($this->error)
+ return;
+
+ $pKey = '';
+ $query = 'CREATE '.($this->isTempTable ? 'TEMPORARY' : '').' TABLE `'.$this->tableName.'` (';
+ $indizes = [];
+
+ if ($this->isGameTable)
+ {
+ $query .= '`idx` INT SIGNED NOT NULL, ';
+ $pKey = 'idx';
+ }
+
+ foreach ($this->format as $name => $type)
+ {
+ $query .= match($type)
+ {
+ 'f' => '`'.$name.'` FLOAT NOT NULL, ',
+ 's' => '`'.$name.'` TEXT NULL, ',
+ 'b' => '`'.$name.'` TINYINT UNSIGNED NOT NULL, ',
+ 'i', 'I', 'n' => '`'.$name.'` INT SIGNED NOT NULL, ',
+ 'u', 'U' => '`'.$name.'` INT SIGNED NOT NULL, ',
+ 'S' => (function ($n) {
+ $buf = '';
+ for ($l = 0; $l < strlen($this->macro['LOC']); $l++)
+ if ($this->macro['LOC'][$l] == 's')
+ $buf .= '`'.$n.'_loc'.$l.'` TEXT NULL, ';
+ return $buf;
+ })($name),
+ default => '' // 'x', 'X', 'd'
+ };
+
+ if ($this->isGameTable)
+ continue;
+
+ if ($type == 'I' || $type == 'U')
+ $indizes[] = $name;
+ if ($type == 'n')
+ $pKey = $name;
+ }
+
+ foreach ($indizes as $i)
+ $query .= 'KEY `idx_'.$i.'` (`'.$i.'`), ';
+
+ if ($pKey)
+ $query .= 'PRIMARY KEY (`'.$pKey.'`) ';
+ else
+ $query = substr($query, 0, -2);
+
+ $query .= ') COLLATE=\'utf8mb4_unicode_ci\' ENGINE=InnoDB';
+
+ DB::Aowow()->qry('DROP TABLE IF EXISTS %n', $this->tableName);
+ DB::Aowow()->qry($query);
+ }
+
+ private function writeToDB() : void
+ {
+ if (!$this->dataBuffer || $this->error)
+ return;
+
+ DB::Aowow()->qry('INSERT INTO %n %m', $this->tableName, $this->dataBuffer);
+
+ $this->dataBuffer = [];
+ }
+
+ private function read() : void
+ {
+ $nRows = reset($this->fileRefs)->nRows; // set to actual value once we have a file handle
+
+ for ($i = 0; $i < $nRows; $i++)
+ {
+ // add 'virtual' enumerator for gt*-dbcs
+ if ($this->isGameTable)
+ $this->dataBuffer['idx'][$i] = $i;
+
+ foreach ($this->fileRefs as $locId => $dbcFile)
+ {
+ // note that the file pointer is already on the first record as the DBCFile reads its own header
+ $row = $dbcFile->readRecord($this->recordFmt);
+
+ foreach ($row as $name => $value)
+ {
+ $type = $this->format[$name];
+
+ // handle locale fields for post 3.3.5a DBCs
+ if ($type === 'S')
+ {
+ for ($k = 0; $k < strlen($this->macro['LOC']); $k++)
+ if ($this->macro['LOC'][$k] === 's')
+ $this->dataBuffer[$name.'_loc'.$k][$i] ??= null;
+
+ $this->dataBuffer[$name.'_loc'.$locId][$i] ??= $dbcFile->getStringFromBlock($value);
+ }
+ if (empty($this->dataBuffer[$name][$i]))
+ {
+ if ($type == 's')
+ $this->dataBuffer[$name][$i] ??= $dbcFile->getStringFromBlock($value);
+ else
+ $this->dataBuffer[$name][$i] = $value;
+ }
+ }
+
+ if (!$this->isLocalized) // one match is enough
+ break;
+ }
+
+ if (count(current($this->dataBuffer)) >= self::MAX_INSERT_ROWS)
+ $this->writeToDB();
+ }
+
+ $this->writeToDB();
+
+ $this->endClean();
+ }
+}
+
+?>
diff --git a/setup/tools/fileGen.class.php b/setup/tools/fileGen.class.php
deleted file mode 100644
index 33927f9d..00000000
--- a/setup/tools/fileGen.class.php
+++ /dev/null
@@ -1,254 +0,0 @@
- [file, path, TCDeps]
- 'searchplugin' => ['aowow.xml', 'static/download/searchplugins/', []],
- 'power' => ['power.js', 'static/widgets/', []],
- 'searchboxScript' => ['searchbox.js', 'static/widgets/', []],
- 'demo' => ['demo.html', 'static/widgets/power/', []],
- 'searchboxBody' => ['searchbox.html', 'static/widgets/searchbox/', []],
- 'realmMenu' => ['profile_all.js', 'static/js/', ['realmlist']],
- 'locales' => ['locale.js', 'static/js/', []],
- 'markup' => ['Markup.js', 'static/js/', []],
- 'itemScaling' => ['item-scaling', 'datasets/', []]
- );
- public static $datasets = array( // name => [AowowDeps, TCDeps, info]
- 'realms' => [null, ['realmlist'], 'datasets/realms'],
- 'statistics' => [null, ['player_levelstats', 'player_classlevelstats'], 'datasets/statistics'],
- 'simpleImg' => [null, null, 'static/images/wow/[icons, Interface, ]/*'],
- 'complexImg' => [null, null, 'static/images/wow/[maps, talents/backgrounds, ]/*'],
- 'talentCalc' => [null, null, 'datasets//talents-*'],
- 'pets' => [['spawns', 'creature'], null, 'datasets//pets'],
- 'talentIcons' => [null, null, 'static/images/wow/talents/icons/*'],
- 'glyphs' => [['items', 'spell'], null, 'datasets//glyphs'],
- 'itemsets' => [['itemset', 'spell'], null, 'datasets//itemsets'],
- 'enchants' => [['items', 'spell', 'itemenchantment'], null, 'datasets//enchants'],
- 'gems' => [['items', 'spell', 'itemenchantment'], null, 'datasets//gems'],
- 'profiler' => [['quests', 'quests_startend', 'spell', 'currencies', 'achievement', 'titles'], null, 'datasets//p-*'],
- 'weightPresets' => [null, null, 'datasets/weight-presets'],
- 'soundfiles' => [['sounds'], null, 'static/wowsounds/*']
- );
-
- public static $defaultExecTime = 30;
-
- private static $reqDirs = array(
- 'static/uploads/screenshots/normal',
- 'static/uploads/screenshots/pending',
- 'static/uploads/screenshots/resized',
- 'static/uploads/screenshots/temp',
- 'static/uploads/screenshots/thumb',
- 'static/uploads/temp/',
- 'static/download/searchplugins/',
- 'static/wowsounds/'
- );
-
- private static $txtConstants = array(
- 'CFG_NAME' => '',
- 'CFG_NAME_SHORT' => '',
- 'CFG_CONTACT_EMAIL' => '',
- 'HOST_URL' => '',
- 'STATIC_URL' => ''
- );
-
- public static function init(int $mode = self::MODE_NORMAL, array $updScripts = []) : bool
- {
- self::$defaultExecTime = ini_get('max_execution_time');
- self::$mode = $mode;
-
- if (!CLISetup::$localeIds)
- {
- CLI::write('No valid locale specified. Check your config or --locales parameter, if used', CLI::LOG_ERROR);
- return false;
- }
-
- // create directory structure
- CLI::write('FileGen::init() - creating required directories');
- $pathOk = 0;
- foreach (self::$reqDirs as $rd)
- if (CLISetup::writeDir($rd))
- $pathOk++;
-
- CLI::write('created '.$pathOk.' extra paths'.($pathOk == count(self::$reqDirs) ? '' : ' with errors'));
- CLI::write();
-
- // handle command prompts
- if (!self::handleCLIOpts($doScripts) && !$updScripts)
- return false;
-
- // check passed subscript names; limit to real scriptNames
- self::$subScripts = array_merge(array_keys(self::$tplFiles), array_keys(self::$datasets));
- if ($doScripts || $updScripts)
- self::$subScripts = array_intersect($doScripts ?: $updScripts, self::$subScripts);
-
- return true;
- }
-
- private static function handleCLIOpts(?array &$doScripts) : bool
- {
- $doScripts = [];
-
- if (CLISetup::getOpt('help') && self::$mode == self::MODE_NORMAL)
- {
- if (in_array('simpleImg', CLISetup::getOpt('build')))
- CLISetup::optHelp(1 << 3);
- else if (in_array('complexImg', CLISetup::getOpt('build')))
- CLISetup::optHelp(1 << 4);
- else
- self::printCLIHelp();
-
- return false;
- }
-
- // required subScripts
- if ($sync = CLISetup::getOpt('sync'))
- {
- foreach (self::$tplFiles as $name => $info)
- if (!empty($info[2]) && array_intersect($sync, $info[2]))
- $doScripts[] = $name;
-
- foreach (self::$datasets as $name => $info)
- {
- // recursive deps from SqlGen
- if (!empty($info[0]) && array_intersect(SqlGen::$subScripts, $info[0]))
- $doScripts[] = $name;
- else if (!empty($info[1]) && array_intersect($sync, $info[1]))
- $doScripts[] = $name;
- }
-
- if (!$doScripts)
- return false;
-
- $doScripts = array_unique($doScripts);
- return true;
- }
- else if (is_array($_ = CLISetup::getOpt('build')))
- {
- $doScripts = $_;
- return true;
- }
-
- return false;
- }
-
- private static function printCLIHelp()
- {
- CLI::write();
- CLI::write(' usage: php aowow --build= [--mpqDataDir: --locales:]', -1, false);
- CLI::write();
- CLI::write(' Compiles files for a given subScript. Existing files are kept by default. Dependencies are taken into account by the triggered calls of --sync and --update', -1, false);
-
- $lines = [['available subScripts', 'affected files', 'TC dependencies', 'AoWoW dependencies']];
- foreach (array_merge(array_keys(self::$tplFiles), array_keys(self::$datasets)) as $s)
- $lines[] = array(
- ' * '.$s,
- isset(self::$tplFiles[$s]) ? self::$tplFiles[$s][1].self::$tplFiles[$s][0] : self::$datasets[$s][2],
- !empty(self::$tplFiles[$s][2]) ? implode(' ', self::$tplFiles[$s][2]) : (!empty(self::$datasets[$s][1]) ? implode(' ', self::$datasets[$s][1]) : ''),
- !empty(self::$datasets[$s][0]) ? implode(' ', self::$datasets[$s][0]) : ''
- );
-
- CLI::writeTable($lines);
- }
-
- public static function generate($key, array $updateIds = [])
- {
- $success = false;
- $reqDBC = [];
-
- if (file_exists('setup/tools/filegen/'.$key.'.func.php'))
- require_once 'setup/tools/filegen/'.$key.'.func.php';
- else if (empty(self::$tplFiles[$key]))
- {
- CLI::write(sprintf(ERR_MISSING_INCL, $key, 'setup/tools/filegen/'.$key.'.func.php', CLI::LOG_ERROR));
- return false;
- }
-
- CLI::write('FileGen::generate() - gathering data for '.$key);
-
- if (!empty(self::$tplFiles[$key]))
- {
- [$file, $destPath, $deps] = self::$tplFiles[$key];
-
- foreach (self::$txtConstants as $n => &$c)
- if (!$c && defined($n))
- $c = constant($n);
-
- if ($content = file_get_contents(self::$tplPath.$file.'.in'))
- {
- // replace constants
- $content = strtr($content, self::$txtConstants);
-
- // check for required auxiliary DBC files
- foreach ($reqDBC as $req)
- if (!CLISetup::loadDBC($req))
- continue;
-
- // must generate content
- // PH format: /*setup:*/
- $funcOK = true;
- if (preg_match_all('/\/\*setup:([\w\-_]+)\*\//i', $content, $m))
- {
- foreach ($m[1] as $func)
- {
- if (function_exists($func))
- $content = str_replace('/*setup:'.$func.'*/', $func(), $content);
- else
- {
- $funcOK = false;
- CLI::write('No function for was registered for placeholder '.$func.'().', CLI::LOG_ERROR);
- if (!array_reduce(get_included_files(), function ($inArray, $itr) use ($func) { return $inArray || false !== strpos($itr, $func); }, false))
- CLI::write('Also, expected include setup/tools/filegen/'.$name.'.func.php was not found.');
- }
- }
- }
-
- if ($content && $funcOK)
- if (CLISetup::writeFile($destPath.$file, $content))
- $success = true;
- }
- else
- CLI::write(sprintf(ERR_READ_FILE, CLI::bold(self::$tplPath.$file.'.in')), CLI::LOG_ERROR);
- }
- else if (!empty(self::$datasets[$key]))
- {
- if (function_exists($key))
- {
- // check for required auxiliary DBC files
- foreach ($reqDBC as $req)
- if (!CLISetup::loadDBC($req))
- return false;
-
- $success = $key($updateIds);
- }
- else
- CLI::write(' - subscript \''.$key.'\' not defined in included file', CLI::LOG_ERROR);
- }
-
- set_time_limit(self::$defaultExecTime); // reset to default for the next script
-
- return $success;
- }
-
- public static function getMode()
- {
- return self::$mode;
- }
-}
-
-?>
diff --git a/setup/tools/filegen/complexImg.func.php b/setup/tools/filegen/complexImg.func.php
deleted file mode 100644
index 056ebb65..00000000
--- a/setup/tools/filegen/complexImg.func.php
+++ /dev/null
@@ -1,751 +0,0 @@
-
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU Affero General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU Affero General Public License for more details.
-
- You should have received a copy of the GNU Affero General Public License
- along with this program. If not, see .
-*/
-
-if (!defined('AOWOW_REVISION'))
- die('illegal access');
-
-if (!CLI)
- die('not in cli mode');
-
- // note: for the sake of simplicity, this function handles all images, that must be stitched together (which are mostly maps)
-
- $reqDBC = ['talenttab', 'chrclasses', 'worldmapoverlay', 'worldmaparea'];
-
- function complexImg()
- {
- $mapWidth = 1002;
- $mapHeight = 668;
- $threshold = 95; // alpha threshold to define subZones: set it too low and you have unspawnable areas inside a zone; set it too high and the border regions overlap
- $runTime = ini_get('max_execution_time');
- $locStr = null;
- $imgPath = CLISetup::$srcDir.'%sInterface/';
- $destDir = 'static/images/wow/';
- $success = true;
- $modeMask = 0x7; // talentBGs, regular maps, spawn-related alphaMaps
- $paths = array(
- 0x16 => ['WorldMap/', true, null],
- 0x01 => ['TalentFrame/', false, null],
- 0x08 => ['Glues/Credits/',false, null]
- );
-
- $createAlphaImage = function($w, $h)
- {
- $img = imagecreatetruecolor($w, $h);
-
- imagesavealpha($img, true);
- imagealphablending($img, false);
-
- $bgColor = imagecolorallocatealpha($img, 0, 0, 0, 127);
- imagefilledrectangle($img, 0, 0, imagesx($img) - 1, imagesy($img) - 1, $bgColor);
-
- imagecolortransparent($img, $bgColor);
- imagealphablending($img, true);
-
- imagecolordeallocate($img, $bgColor);
-
- return $img;
- };
-
- // prefer manually converted PNG files (as the imagecreatefromblp-script has issues with some formats)
- // alpha channel issues observed with locale deDE Hilsbrad and Elwynn - maps
- // see: https://github.com/Kanma/BLPConverter
- $loadImageFile = function($path)
- {
- $result = null;
-
- $file = $path.'.png';
- if (CLISetup::fileExists($file))
- {
- CLI::write('manually converted png file present for '.$path.'.', CLI::LOG_INFO);
- $result = imagecreatefrompng($file);
- }
-
- if (!$result)
- {
- $file = $path.'.blp';
- if (CLISetup::fileExists($file))
- $result = imagecreatefromblp($file);
- }
-
- return $result;
- };
-
- $assembleImage = function($baseName, $order, $w, $h) use ($loadImageFile)
- {
- $dest = imagecreatetruecolor($w, $h);
- imagesavealpha($dest, true);
- imagealphablending($dest, false);
-
- $_h = $h;
- foreach ($order as $y => $row)
- {
- $_w = $w;
- foreach ($row as $x => $suffix)
- {
- $src = $loadImageFile($baseName.$suffix);
- if (!$src)
- {
- CLI::write(' - complexImg: tile '.$baseName.$suffix.'.blp missing.', CLI::LOG_ERROR);
- unset($dest);
- return null;
- }
-
- imagecopyresampled($dest, $src, 256 * $x, 256 * $y, 0, 0, min($_w, 256), min($_h, 256), min($_w, 256), min($_h, 256));
- $_w -= 256;
-
- unset($src);
- }
- $_h -= 256;
- }
-
- return $dest;
- };
-
- $writeImage = function($name, $ext, $src, $w, $h, $done)
- {
- $ok = false;
- $dest = imagecreatetruecolor($w, $h);
- imagesavealpha($dest, true);
- imagealphablending($dest, false);
- imagecopyresampled($dest, $src, 0, 0, 0, 0, $w, $h, imagesx($src), imagesy($src));
-
- switch ($ext)
- {
- case 'jpg':
- $ok = imagejpeg($dest, $name.'.'.$ext, 85);
- break;
- case 'png':
- $ok = imagepng($dest, $name.'.'.$ext);
- break;
- default:
- CLI::write($done.' - unsupported file fromat: '.$ext, CLI::LOG_WARN);
- }
-
- imagedestroy($dest);
-
- if ($ok)
- {
- chmod($name.'.'.$ext, Util::FILE_ACCESS);
- CLI::write($done.' - image '.$name.'.'.$ext.' written', CLI::LOG_OK);
- }
- else
- CLI::write($done.' - could not create image '.$name.'.'.$ext, CLI::LOG_ERROR);
-
- return $ok;
- };
-
- $createSpawnMap = function($img, $zoneId) use ($mapHeight, $mapWidth, $threshold)
- {
- CLI::write(' - creating spawn map');
-
- $tmp = imagecreate(1000, 1000);
- $cbg = imagecolorallocate($tmp, 255, 255, 255);
- $cfg = imagecolorallocate($tmp, 0, 0, 0);
-
- for ($y = 0; $y < 1000; $y++)
- {
- for ($x = 0; $x < 1000; $x++)
- {
- $a = imagecolorat($img, ($x * $mapWidth) / 1000, ($y * $mapHeight) / 1000) >> 24;
- imagesetpixel($tmp, $x, $y, $a < $threshold ? $cfg : $cbg);
- }
- }
-
- imagepng($tmp, 'setup/generated/alphaMaps/' . $zoneId . '.png');
-
- imagecolordeallocate($tmp, $cbg);
- imagecolordeallocate($tmp, $cfg);
- imagedestroy($tmp);
- };
-
- $checkSourceDirs = function($sub) use ($imgPath, &$paths, $modeMask)
- {
- $hasMissing = false;
- foreach ($paths as $idx => [$subDir, $isLocalized, $realPath])
- {
- if ($realPath && !$isLocalized)
- continue;
-
- $p = sprintf($imgPath, $sub).$subDir;
- if (CLISetup::fileExists($p))
- {
- if ($isLocalized)
- $paths[$idx][2][substr($sub, 0, -1)] = $p;
- else
- $paths[$idx][2] = $p;
- }
- else
- $hasMissing = true;
- }
-
- return !$hasMissing;
- };
-
- // do not change order of params!
- $o = CLISetup::getOpt('talentbgs', 'maps', 'spawn-maps', 'artwork', 'area-maps');
- $m = 0x0;
- $i = 0;
- foreach ($o as $k => $v)
- {
- if ($v)
- $m |= 1 << $i;
- $i++;
- }
-
- if ($m)
- $modeMask = $m;
-
- foreach ($paths as $mode => $__)
- if (!($mode & $modeMask))
- unset($paths[$mode]);
-
- foreach (CLISetup::$expectedPaths as $xp => $locId)
- {
- if (!in_array($locId, CLISetup::$localeIds))
- continue;
-
- if ($xp) // if in subDir add trailing slash
- $xp .= '/';
-
- $checkSourceDirs($xp); // do not break; maps are localized
- }
-
- $locList = [];
- foreach (CLISetup::$expectedPaths as $xp => $locId)
- if (in_array($locId, CLISetup::$localeIds))
- $locList[] = $xp;
-
- CLI::write('required resources overview:', CLI::LOG_INFO);
- foreach ($paths as [$path, $isLocalized, $realPath])
- {
- if (!$realPath)
- CLI::write(CLI::red('MISSING').' - '.str_pad($path, 14).' @ '.sprintf($imgPath, '['.implode(',', $locList).']/').$path);
- else if ($isLocalized)
- {
- $foundLoc = [];
- foreach (CLISetup::$localeIds as $locId)
- foreach (CLISetup::$expectedPaths as $xp => $lId)
- if ($locId == $lId && isset($realPath[$xp]) && !isset($foundLoc[$locId]))
- $foundLoc[$locId] = $xp;
-
- if ($diff = array_diff(CLISetup::$localeIds, array_keys($foundLoc)))
- {
- $buff = [];
- foreach ($diff as $d)
- $buff[] = CLI::yellow(Util::$localeStrings[$d]);
- foreach ($foundLoc as $str)
- $buff[] = CLI::green($str);
-
- CLI::write(CLI::yellow('PARTIAL').' - '.str_pad($path, 14).' @ '.sprintf($imgPath, '['.implode(',', $buff).']/').$path);
- }
- else
- CLI::write(CLI::green(' FOUND ').' - '.str_pad($path, 14).' @ '.sprintf($imgPath, '['.implode(',', $foundLoc).']/').$path);
- }
- else
- CLI::write(CLI::green(' FOUND ').' - '.str_pad($path, 14).' @ '.$realPath);
- }
-
- CLI::write();
-
- // if no subdir had sufficient data, diaf
- if (count(array_filter(array_column($paths, 2))) != count($paths))
- {
- CLI::write('one or more required directories are missing:', CLI::LOG_ERROR);
- foreach ($missing as $m)
- CLI::write(' - '.$m, CLI::LOG_ERROR);
-
- return;
- }
- else
- sleep(1);
-
- /**************/
- /* TalentTabs */
- /**************/
-
- if ($modeMask & 0x01)
- {
- if (CLISetup::writeDir($destDir.'hunterpettalents/') && CLISetup::writeDir($destDir.'talents/backgrounds/'))
- {
- // [classMask, creatureFamilyMask, tabNr, textureStr]
-
- $tTabs = DB::Aowow()->select('SELECT tt.creatureFamilyMask, tt.textureFile, tt.tabNumber, cc.fileString FROM dbc_talenttab tt LEFT JOIN dbc_chrclasses cc ON cc.id = (LOG(2, tt.classMask) + 1)');
- $order = array(
- ['-TopLeft', '-TopRight'],
- ['-BottomLeft', '-BottomRight']
- );
-
- if ($tTabs)
- {
- $sum = 0;
- $total = count($tTabs);
- CLI::write('Processing '.$total.' files from TalentFrame/ ...');
-
- foreach ($tTabs as $tt)
- {
- ini_set('max_execution_time', 30); // max 30sec per image (loading takes the most time)
- $sum++;
- $done = ' - '.str_pad($sum.'/'.$total, 8).str_pad('('.number_format($sum * 100 / $total, 2).'%)', 9);
-
- if ($tt['creatureFamilyMask']) // is PetCalc
- {
- $size = [244, 364];
- $name = $destDir.'hunterpettalents/bg_'.(log($tt['creatureFamilyMask'], 2) + 1);
- }
- else
- {
- $size = [204, 554];
- $name = $destDir.'talents/backgrounds/'.strtolower($tt['fileString']).'_'.($tt['tabNumber'] + 1);
- }
-
- if (!CLISetup::getOpt('force') && file_exists($name.'.jpg'))
- {
- CLI::write($done.' - file '.$name.'.jpg was already processed');
- continue;
- }
-
- $im = $assembleImage($paths[0x1][2].'/'.$tt['textureFile'], $order, 256 + 44, 256 + 75);
- if (!$im)
- {
- CLI::write(' - could not assemble file '.$tt['textureFile'], CLI::LOG_ERROR);
- continue;
- }
-
- if (!$writeImage($name, 'jpg', $im, $size[0], $size[1], $done))
- $success = false;
- }
- }
- else
- $success = false;
-
- ini_set('max_execution_time', $runTime);
- }
- else
- $success = false;
- }
-
- /************/
- /* Worldmap */
- /************/
-
- if ($modeMask & 0x16)
- {
- $mapDirs = array(
- ['maps/%snormal/', 'jpg', 488, 325],
- ['maps/%soriginal/', 'jpg', 0, 0], // 1002, 668
- ['maps/%ssmall/', 'jpg', 224, 149],
- ['maps/%szoom/', 'jpg', 772, 515]
- );
-
- // as the js expects them
- $baseLevelFix = array(
- // WotLK maps
- // Halls of Stone; The Nexus; Violet Hold; Gundrak; Obsidian Sanctum; Eye of Eternity; Vault of Archavon; Trial of the Champion; The Forge of Souls; Pit of Saron; Halls of Reflection
- 4264 => 1, 4265 => 1, 4415 => 1, 4416 => 1, 4493 => 0, 4500 => 1, 4603 => 1, 4723 => 1, 4809 => 1, 4813 => 1, 4820 => 1,
- // Cata maps for WotLK instances
- // TheStockade; TheBloodFurnace; Ragefire; TheUnderbog; TheBotanica; WailingCaverns; TheSlavePens; TheShatteredHalls; HellfireRamparts; RazorfenDowns; RazorfenKraul; ManaTombs
- // ShadowLabyrinth; TheTempleOfAtalHakkar (simplified layout); BlackTemple; TempestKeep; MoltenCore; GruulsLair; CoilfangReservoir; MagtheridonsLair; OnyxiasLair; SunwellPlateau;
- 717 => 1, 3713 => 1, 2437 => 1, 3716 => 1, 3847 => 1, 718 => 1, 3717 => 1, 3714 => 1, 3562 => 1, 722 => 1, 491 => 1, 3792 => 1,
- 3789 => 1, 1477 => 1, 3959 => 0, 3845 => 1, 2717 => 1, 3923 => 1, 3607 => 1, 3836 => 1, 2159 => 1, 4075 => 0
- );
-
- $wmo = DB::Aowow()->select('SELECT *, worldMapAreaId AS ARRAY_KEY, id AS ARRAY_KEY2 FROM dbc_worldmapoverlay WHERE textureString <> ""');
- $wma = DB::Aowow()->select('SELECT * FROM dbc_worldmaparea');
- if (!$wma || !$wmo)
- {
- $success = false;
- CLI::write(' - could not read required dbc files: WorldMapArea.dbc ['.count($wma).' entries]; WorldMapOverlay.dbc ['.count($wmo).' entries]', CLI::LOG_ERROR);
- return;
- }
-
- // fixups...
- foreach ($wma as &$a)
- {
- if ($a['areaId'])
- continue;
-
- switch ($a['id'])
- {
- case 13: $a['areaId'] = -6; break; // Kalimdor
- case 14: $a['areaId'] = -3; break; // Eastern Kingdoms
- case 466: $a['areaId'] = -2; break; // Outland
- case 485: $a['areaId'] = -5; break; // Northrend
- }
- }
- array_unshift($wma, ['id' => -1, 'areaId' => -1, 'nameINT' => 'World'], ['id' => -4, 'areaId' => -4, 'nameINT' => 'Cosmic']);
-
- $sumMaps = count(CLISetup::$localeIds) * count($wma);
-
- CLI::write('Processing '.$sumMaps.' files from WorldMap/ ...');
-
- foreach (CLISetup::$localeIds as $progressLoc => $l)
- {
- // create destination directories
- $dirError = false;
- foreach ($mapDirs as $md)
- if (!CLISetup::writeDir($destDir . sprintf($md[0], strtolower(Util::$localeStrings[$l]).'/')))
- $dirError = true;
-
- if ($modeMask & 0x04)
- if (!CLISetup::writeDir('setup/generated/alphaMaps'))
- $dirError = true;
-
- if ($dirError)
- {
- $success = false;
- CLI::write(' - complexImg: could not create map directories for locale '.$l.'. skipping...', CLI::LOG_ERROR);
- continue;
- }
-
-
- // source for mapFiles
- $mapSrcDir = null;
- $locDirs = array_reverse(array_filter(CLISetup::$expectedPaths, function($var) use ($l) { return !$var || $var == $l; }), true);
- foreach ($locDirs as $mapLoc => $__)
- {
- if(!isset($paths[0x16][2][$mapLoc]))
- continue;
-
- $p = sprintf($imgPath, $mapLoc.'/').$paths[0x16][0];
- if (CLISetup::fileExists($p))
- {
- CLI::write(' - using files from '.($mapLoc ?: '/').' for locale '.Util::$localeStrings[$l], CLI::LOG_INFO);
- $mapSrcDir = $p.'/';
- break;
- }
- }
-
- if ($mapSrcDir === null)
- {
- $success = false;
- CLI::write(' - no suitable localized map files found for locale '.$l, CLI::LOG_ERROR);
- continue;
- }
-
-
- foreach ($wma as $progressArea => $areaEntry)
- {
- $curMap = $progressArea + count($wma) * $progressLoc;
- $progress = ' - ' . str_pad($curMap.'/'.($sumMaps), 10) . str_pad('('.number_format($curMap * 100 / $sumMaps, 2).'%)', 9);
-
- $wmaId = $areaEntry['id'];
- $zoneId = $areaEntry['areaId'];
- $textureStr = $areaEntry['nameINT'];
-
- $path = $mapSrcDir.$textureStr;
- if (!CLISetup::fileExists($path))
- {
- $success = false;
- CLI::write('worldmap file '.$path.' missing for selected locale '.Util::$localeStrings[$l], CLI::LOG_ERROR);
- continue;
- }
-
- $fmt = array(
- [1, 2, 3, 4],
- [5, 6, 7, 8],
- [9, 10, 11, 12]
- );
-
- CLI::write($textureStr . " [" . $zoneId . "]");
-
- $overlay = $createAlphaImage($mapWidth, $mapHeight);
-
- // zone has overlays (is in open world; is not multiLeveled)
- if (isset($wmo[$wmaId]))
- {
- CLI::write(' - area has '.count($wmo[$wmaId]).' overlays');
-
- foreach ($wmo[$wmaId] as &$row)
- {
- $i = 1;
- $y = 0;
- while ($y < $row['h'])
- {
- $x = 0;
- while ($x < $row['w'])
- {
- $img = $loadImageFile($path . '/' . $row['textureString'] . $i);
- if (!$img)
- {
- CLI::write(' - complexImg: tile '.$path.'/'.$row['textureString'].$i.'.blp missing.', CLI::LOG_ERROR);
- break 2;
- }
-
- imagecopy($overlay, $img, $row['x'] + $x, $row['y'] + $y, 0, 0, imagesx($img), imagesy($img));
-
- // prepare subzone image
- if ($modeMask & 0x10)
- {
- if (!isset($row['maskimage']))
- {
- $row['maskimage'] = $createAlphaImage($row['w'], $row['h']);
- $row['maskcolor'] = imagecolorallocatealpha($row['maskimage'], 255, 64, 192, 64);
- }
-
- for ($my = 0; $my < imagesy($img); $my++)
- for ($mx = 0; $mx < imagesx($img); $mx++)
- if ((imagecolorat($img, $mx, $my) >> 24) < $threshold)
- imagesetpixel($row['maskimage'], $x + $mx, $y + $my, $row['maskcolor']);
- }
-
- imagedestroy($img);
-
- $x += 256;
- $i++;
- }
- $y += 256;
- }
- }
-
- // create spawn-maps if wanted
- if ($modeMask & 0x04)
- $createSpawnMap($overlay, $zoneId);
- }
-
- // check, if the current zone is multiLeveled
- // if there are also files present without layer-suffix assume them as layer: 0
- $multiLeveled = false;
- $multiLevel = 0;
- do
- {
- if (!CLISetup::filesInPath('/'.$textureStr.'\/'.$textureStr.($multiLevel + 1).'_\d\.blp/i', true))
- break;
-
- $multiLevel++;
- $multiLeveled = true;
- }
- while ($multiLevel < 18); // Karazhan has 17 frickin floors
-
- // check if we can create base map anyway
- $file = $path.'/'.$textureStr.'1.blp';
- $hasBaseMap = CLISetup::fileExists($file);
-
- CLI::write(' - area has '.($multiLeveled ? $multiLevel . ' levels' : 'only base level'));
-
- $map = null;
- for ($i = 0; $i <= $multiLevel; $i++)
- {
- ini_set('max_execution_time', 120); // max 120sec per image
-
- $file = $path.'/'.$textureStr;
-
- if (!$i && !$hasBaseMap)
- continue;
-
- // if $multiLeveled also suffix -0 to baseMap if it exists
- if ($i && $multiLeveled)
- $file .= $i.'_';
-
- $doSkip = 0x0;
- $outFile = [];
-
- foreach ($mapDirs as $idx => $info)
- {
- $outFile[$idx] = $destDir . sprintf($info[0], strtolower(Util::$localeStrings[$l]).'/') . $zoneId;
-
- $floor = $i;
- if ($zoneId == 4100) // ToCStratholme: map order fix
- $floor += 1;
-
- if ($multiLeveled && !(isset($baseLevelFix[$zoneId]) && $i == $baseLevelFix[$zoneId]))
- $outFile[$idx] .= '-'.$floor;
-
- if (!CLISetup::getOpt('force') && file_exists($outFile[$idx].'.'.$info[1]))
- {
- CLI::write($progress.' - file '.$outFile[$idx].'.'.$info[1].' was already processed');
- $doSkip |= (1 << $idx);
- }
- }
-
- if ($doSkip == 0xF)
- continue;
-
- $map = $assembleImage($file, $fmt, $mapWidth, $mapHeight);
- if (!$map)
- {
- $success = false;
- CLI::write(' - could not create image resource for map '.$zoneId.($multiLevel ? ' level '.$i : ''));
- continue;
- }
-
- if (!$multiLeveled)
- {
- imagecopymerge($map, $overlay, 0, 0, 0, 0, imagesx($overlay), imagesy($overlay), 100);
- imagedestroy($overlay);
- }
-
- // create map
- if ($modeMask & 0x02)
- {
- foreach ($mapDirs as $idx => $info)
- {
- if ($doSkip & (1 << $idx))
- continue;
-
- if (!$writeImage($outFile[$idx], $info[1], $map, $info[2] ?: $mapWidth, $info[3] ?: $mapHeight, $progress))
- $success = false;
- }
- }
- }
-
- // also create subzone-maps
- if ($map && isset($wmo[$wmaId]) && $modeMask & 0x10)
- {
- foreach ($wmo[$wmaId] as &$row)
- {
- $doSkip = 0x0;
- $outFile = [];
-
- foreach ($mapDirs as $idx => $info)
- {
- $outFile[$idx] = $destDir . sprintf($info[0], strtolower(Util::$localeStrings[$l]).'/') . $row['areaTableId'];
- if (!CLISetup::getOpt('force') && file_exists($outFile[$idx].'.'.$info[1]))
- {
- CLI::write($progress.' - file '.$outFile[$idx].'.'.$info[1].' was already processed');
- $doSkip |= (1 << $idx);
- }
- }
-
- if ($doSkip == 0xF)
- continue;
-
- $subZone = imagecreatetruecolor($mapWidth, $mapHeight);
- imagecopy($subZone, $map, 0, 0, 0, 0, imagesx($map), imagesy($map));
- imagecopy($subZone, $row['maskimage'], $row['x'], $row['y'], 0, 0, imagesx($row['maskimage']), imagesy($row['maskimage']));
-
- foreach ($mapDirs as $idx => $info)
- {
- if ($doSkip & (1 << $idx))
- continue;
-
- if (!$writeImage($outFile[$idx], $info[1], $subZone, $info[2] ?: $mapWidth, $info[3] ?: $mapHeight, $progress))
- $success = false;
- }
-
- imagedestroy($subZone);
- }
- }
-
- if ($map)
- imagedestroy($map);
-
- // this takes a while; ping mysql just in case
- DB::Aowow()->selectCell('SELECT 1');
- }
- }
- }
-
- /***********/
- /* Credits */
- /***********/
-
- if ($modeMask & 0x08) // optional tidbits (not used by default)
- {
- if (CLISetup::writeDir($destDir.'Interface/Glues/Credits/'))
- {
- // tile ordering
- $order = array(
- 1 => array(
- [1]
- ),
- 2 => array(
- [1],
- [2]
- ),
- 4 => array(
- [1, 2],
- [3, 4]
- ),
- 6 => array(
- [1, 2, 3],
- [4, 5, 6]
- ),
- 8 => array(
- [1, 2, 3, 4],
- [5, 6, 7, 8]
- )
- );
-
- $imgGroups = [];
- $files = CLISetup::filesInPath('/'.str_replace('/', '\\/', $paths[0x8][2]).'/i', true);
- foreach ($files as $f)
- {
- if (preg_match('/([^\/]+)(\d).blp/i', $f, $m))
- {
- if ($m[1] && $m[2])
- {
- if (!isset($imgGroups[$m[1]]))
- $imgGroups[$m[1]] = $m[2];
- else if ($imgGroups[$m[1]] < $m[2])
- $imgGroups[$m[1]] = $m[2];
- }
- }
- }
-
- // errör-korrekt
- $imgGroups['Desolace'] = 4;
- $imgGroups['BloodElf_Female'] = 6;
-
- $total = count($imgGroups);
- $sum = 0;
-
- CLI::write('Processing '.$total.' files from Glues/Credits/...');
-
- foreach ($imgGroups as $file => $fmt)
- {
- ini_set('max_execution_time', 30); // max 30sec per image (loading takes the most time)
-
- $sum++;
- $done = ' - '.str_pad($sum.'/'.$total, 8).str_pad('('.number_format($sum * 100 / $total, 2).'%)', 9);
- $name = $destDir.'Interface/Glues/Credits/'.$file;
-
- if (!CLISetup::getOpt('force') && file_exists($name.'.png'))
- {
- CLI::write($done.' - file '.$name.'.png was already processed');
- continue;
- }
-
- if (!isset($order[$fmt]))
- {
- CLI::write(' - pattern for file '.$name.' not set. skipping', CLI::LOG_WARN);
- continue;
- }
-
- $im = $assembleImage($paths[0x8][2].'/'.$file, $order[$fmt], count($order[$fmt][0]) * 256, count($order[$fmt]) * 256);
- if (!$im)
- {
- CLI::write(' - could not assemble file '.$name, CLI::LOG_ERROR);
- continue;
- }
-
- if (!$writeImage($name, 'png', $im, count($order[$fmt][0]) * 256, count($order[$fmt]) * 256, $done))
- $success = false;
- }
-
- ini_set('max_execution_time', $runTime);
- }
- else
- $success = false;
- }
-
- return $success;
- }
-
-?>
diff --git a/setup/tools/filegen/demo.ss.php b/setup/tools/filegen/demo.ss.php
new file mode 100644
index 00000000..30470685
--- /dev/null
+++ b/setup/tools/filegen/demo.ss.php
@@ -0,0 +1,24 @@
+ [[], CLISetup::ARGV_PARAM, 'Fills powered tooltip demo page (static/widgets/power/demo.html) with site variables.']
+ );
+
+ protected $fileTemplateSrc = ['demo.html.in'];
+ protected $fileTemplateDest = ['static/widgets/power/demo.html'];
+});
+
+?>
diff --git a/setup/tools/filegen/enchants.func.php b/setup/tools/filegen/enchants.ss.php
similarity index 66%
rename from setup/tools/filegen/enchants.func.php
rename to setup/tools/filegen/enchants.ss.php
index 4727df56..bc6521fc 100644
--- a/setup/tools/filegen/enchants.func.php
+++ b/setup/tools/filegen/enchants.ss.php
@@ -1,5 +1,7 @@
[[], CLISetup::ARGV_PARAM, 'Compiles enchantment effects to file for the item comparison tool and profiler tool.']
+ );
- function enchants()
+ protected $setupAfter = [['items', 'spell', 'itemenchantment', 'icons', 'source'], []];
+ protected $requiredDirs = ['datasets/'];
+ protected $localized = true;
+
+ public function generate() : bool
{
// from g_item_slots: 13:"One-Hand", 15:"Ranged", 17:"Two-Hand",
$slotPointer = [13, 17, 15, 15, 13, 17, 17, 13, 17, null, 17, null, null, 13, null, 13, null, null, null, null, 17];
- $castItems = [];
- $successs = true;
- $enchantSpells = DB::Aowow()->select('
- SELECT
- s.id AS ARRAY_KEY,
- effect1MiscValue,
- equippedItemClass, equippedItemInventoryTypeMask, equippedItemSubClassMask,
- skillLine1,
- IFNULL(i.name, "inv_misc_questionmark") AS iconString,
- name_loc0, name_loc2, name_loc3, name_loc4, name_loc6, name_loc8
- FROM
- ?_spell s
- LEFT JOIN
- ?_icons i ON i.id = s.iconId
- WHERE
- effect1Id = ?d AND
- name_loc0 NOT LIKE "QA%"'
- , 53); // enchantItemPermanent && !qualityAssurance
- // check directory-structure
- foreach (Util::$localeStrings as $dir)
- if (!CLISetup::writeDir('datasets/'.$dir))
- $success = false;
+ $castItems = [];
+ $enchantSpells = DB::Aowow()->selectAssoc(
+ 'SELECT s.`id` AS ARRAY_KEY,
+ `effect1MiscValue`,
+ `equippedItemClass`, `equippedItemInventoryTypeMask`, `equippedItemSubClassMask`,
+ `skillLine1`,
+ IFNULL(i.`name`, %s) AS "iconString",
+ `name_loc0`, `name_loc2`, `name_loc3`, `name_loc4`, `name_loc6`, `name_loc8`
+ FROM ::spell s
+ LEFT JOIN ::icons i ON i.`id` = s.`iconId`
+ WHERE `effect1Id` = %i AND
+ `name_loc0` NOT LIKE "QA%"',
+ DEFAULT_ICON, SPELL_EFFECT_ENCHANT_ITEM
+ );
$enchIds = array_column($enchantSpells, 'effect1MiscValue');
- $enchantments = new EnchantmentList(array(['id', $enchIds], CFG_SQL_LIMIT_NONE));
+ $enchantments = new EnchantmentList(array(['id', $enchIds]));
if ($enchantments->error)
{
- CLI::write('Required table ?_itemenchantment seems to be empty! Leaving enchants()...', CLI::LOG_ERROR);
+ CLI::write('[enchants] Required table ::itemenchantment seems to be empty!', CLI::LOG_ERROR);
CLI::write();
return false;
}
- $castItems = new ItemList(array(['spellId1', array_keys($enchantSpells)], ['src.typeId', null, '!'], CFG_SQL_LIMIT_NONE));
-
- foreach (CLISetup::$localeIds as $lId)
+ $castItems = new ItemList(array(['spellId1', array_keys($enchantSpells)], ['src.typeId', null, '!']));
+ if ($castItems->error)
{
- User::useLocale($lId);
- Lang::load(Util::$localeStrings[$lId]);
+ CLI::write('[enchants] Required table ::items seems to be empty!', CLI::LOG_ERROR);
+ CLI::write();
+ return false;
+ }
+
+ foreach (CLISetup::$locales as $loc)
+ {
+ Lang::load($loc);
$enchantsOut = [];
foreach ($enchantSpells as $esId => $es)
@@ -106,20 +111,20 @@ if (!CLI)
$eId = $es['effect1MiscValue'];
if (!$enchantments->getEntry($eId))
{
- CLI::write(' * could not find enchantment #'.$eId.' referenced by spell #'.$esId, CLI::LOG_WARN);
+ CLI::write('[enchants] * could not find enchantment #'.$eId.' referenced by spell #'.$esId, CLI::LOG_WARN);
continue;
}
// slots have to be recalculated
$slot = 0;
- if ($es['equippedItemClass'] == 4) // armor
+ if ($es['equippedItemClass'] == ITEM_CLASS_ARMOR)
{
if ($invType = $es['equippedItemInventoryTypeMask'])
$slot = $invType >> 1;
else /* if (equippedItemSubClassMask == 64) */ // shields have it their own way <_<
- $slot = (1 << (14 - 1));
+ $slot = (1 << (INVTYPE_SHIELD - 1));
}
- else if ($es['equippedItemClass'] == 2) // weapon
+ else if ($es['equippedItemClass'] == ITEM_CLASS_WEAPON)
{
foreach ($slotPointer as $i => $sp)
{
@@ -129,7 +134,7 @@ if (!CLI)
if ((1 << $i) & $es['equippedItemSubClassMask'])
{
if ($sp == 13) // also mainHand & offHand *siiigh*
- $slot |= ((1 << (21 - 1)) | (1 << (22 - 1)));
+ $slot |= ((1 << (INVTYPE_WEAPONMAINHAND - 1)) | (1 << (INVTYPE_WEAPONOFFHAND - 1)));
$slot |= (1 << ($sp - 1));
}
@@ -145,7 +150,7 @@ if (!CLI)
'skill' => -1, // modified if skill
'slots' => [], // determined per spell but set per item
'enchantment' => $enchantments->getField('name', true),
- 'jsonequip' => $enchantments->getStatGain(),
+ 'jsonequip' => $enchantments->getStatGainForCurrent(),
'temp' => 0, // always 0
'classes' => 0, // modified by item
'gearscore' => 0 // set later
@@ -181,7 +186,7 @@ if (!CLI)
if ($cI->getField('spellId1') != $esId)
continue;
- $isScroll = substr($cI->getField('name_loc0'), 0, 17) == 'Scroll of Enchant';
+ $isScroll = $cI->getField('class') == ITEM_CLASS_CONSUMABLE && $cI->getField('subClass') == ITEM_SUBCLASS_ITEM_ENHANCEMENT && $cI->getField('pickUpSoundId') == 1192;
if ($s = Util::getEnchantmentScore($cI->getField('itemLevel'), $isScroll ? -1 : $cI->getField('quality'), !!$enchantments->getField('skillLevel'), $eId))
if ($s > $ench['gearscore'])
@@ -198,10 +203,10 @@ if (!CLI)
if ($cI->getField('quality') > $ench['quality'])
$ench['quality'] = $cI->getField('quality');
- if ($cI->getField('requiredClass') > 0)
+ if ($rc = $cI->getField('requiredClass'))
{
- $ench['classes'] = $cI->getField('requiredClass');
- $ench['jsonequip']['classes'] = $cI->getField('requiredClass');
+ $ench['classes'] = $rc;
+ $ench['jsonequip']['classes'] = $rc;
}
if (!isset($ench['jsonequip']['reqlevel']))
@@ -250,13 +255,14 @@ if (!CLI)
$ench[$k] = $v[0];
$toFile = "var g_enchants = ".Util::toJSON($enchantsOut).";";
- $file = 'datasets/'.User::$localeString.'/enchants';
+ $file = 'datasets/'.$loc->json().'/enchants';
if (!CLISetup::writeFile($file, $toFile))
- $success = false;
+ $this->success = false;
}
- return $successs;
+ return $this->success;
}
+});
?>
diff --git a/setup/tools/filegen/gems.func.php b/setup/tools/filegen/gems.func.php
deleted file mode 100644
index f1719e7e..00000000
--- a/setup/tools/filegen/gems.func.php
+++ /dev/null
@@ -1,102 +0,0 @@
-Select(
- 'SELECT i.id AS itemId,
- i.name_loc0, i.name_loc2, i.name_loc3, i.name_loc4, i.name_loc6, i.name_loc8,
- IF (i.id < 36000 OR i.itemLevel < 70, 1 , 2) AS expansion,
- i.quality,
- ic.name AS icon,
- i.gemEnchantmentId AS enchId,
- i.gemColorMask AS colors,
- i.requiredSkill,
- i.itemLevel
- FROM ?_items i
- JOIN ?_icons ic ON ic.id = i.iconId
- WHERE i.gemEnchantmentId <> 0
- ORDER BY i.id DESC');
- $success = true;
-
- // check directory-structure
- foreach (Util::$localeStrings as $dir)
- if (!CLISetup::writeDir('datasets/'.$dir))
- $success = false;
-
- $enchIds = [];
- foreach ($gems as $pop)
- $enchIds[] = $pop['enchId'];
-
- $enchantments = new EnchantmentList(array(['id', $enchIds], CFG_SQL_LIMIT_NONE));
- if ($enchantments->error)
- {
- CLI::write('Required table ?_itemenchantment seems to be empty! Leaving gems()...', CLI::LOG_ERROR);
- CLI::write();
- return false;
- }
-
- foreach (CLISetup::$localeIds as $lId)
- {
- set_time_limit(5);
-
- User::useLocale($lId);
- Lang::load(Util::$localeStrings[$lId]);
-
- $gemsOut = [];
- foreach ($gems as $pop)
- {
- if (!$enchantments->getEntry($pop['enchId']))
- {
- CLI::write(' * could not find enchantment #'.$pop['enchId'].' referenced by item #'.$gem['itemId'], CLI::LOG_WARN);
- continue;
- }
-
- $gemsOut[$pop['itemId']] = array(
- 'name' => Util::localizedString($pop, 'name'),
- 'quality' => $pop['quality'],
- 'icon' => strToLower($pop['icon']),
- 'enchantment' => $enchantments->getField('name', true),
- 'jsonequip' => $enchantments->getStatGain(),
- 'colors' => $pop['colors'],
- 'expansion' => $pop['expansion'],
- 'gearscore' => Util::getGemScore($pop['itemLevel'], $pop['quality'], $pop['requiredSkill'] == 755, $pop['itemId'])
- );
- }
-
- $toFile = "var g_gems = ".Util::toJSON($gemsOut).";";
- $file = 'datasets/'.User::$localeString.'/gems';
-
- if (!CLISetup::writeFile($file, $toFile))
- $success = false;
- }
-
- return $success;
- }
-
-?>
diff --git a/setup/tools/filegen/gems.ss.php b/setup/tools/filegen/gems.ss.php
new file mode 100644
index 00000000..6ab27d67
--- /dev/null
+++ b/setup/tools/filegen/gems.ss.php
@@ -0,0 +1,102 @@
+ [[], CLISetup::ARGV_PARAM, 'Compiles gems to file for the item comparison tool and profiler tool.']
+ );
+
+ protected $setupAfter = [['items', 'itemenchantment', 'icons'], []];
+ protected $requiredDirs = ['datasets/'];
+ protected $localized = true;
+
+ public function generate() : bool
+ {
+ // sketchy, but should work
+ // id < 36'000 || ilevel < 70 ? BC : WOTLK
+ $gems = DB::Aowow()->selectAssoc(
+ 'SELECT i.`id` AS "itemId",
+ i.`name_loc0`, i.`name_loc2`, i.`name_loc3`, i.`name_loc4`, i.`name_loc6`, i.`name_loc8`,
+ IF (i.`id` < 36000 OR i.`itemLevel` < 70, %i, %i) AS "expansion",
+ i.`quality`,
+ ic.`name` AS "icon",
+ i.`gemEnchantmentId` AS "enchId",
+ i.`gemColorMask` AS "colors",
+ i.`requiredSkill`,
+ i.`itemLevel`
+ FROM ::items i
+ JOIN ::icons ic ON ic.`id` = i.`iconId`
+ WHERE i.`gemEnchantmentId` <> 0
+ ORDER BY i.`id` DESC',
+ EXP_BC, EXP_WOTLK
+ );
+
+ $enchantments = new EnchantmentList(array(['id', array_column($gems, 'enchId')]));
+ if ($enchantments->error)
+ {
+ CLI::write('[gems] Required table ::itemenchantment seems to be empty!', CLI::LOG_ERROR);
+ CLI::write();
+ return false;
+ }
+
+ foreach (CLISetup::$locales as $loc)
+ {
+ set_time_limit(5);
+
+ Lang::load($loc);
+
+ $gemsOut = [];
+ foreach ($gems as $g)
+ {
+ if (!$enchantments->getEntry($g['enchId']))
+ {
+ CLI::write('[gems] * could not find enchantment #'.$g['enchId'].' referenced by item #'.$g['itemId'], CLI::LOG_WARN);
+ continue;
+ }
+
+ $gemsOut[$g['itemId']] = array(
+ 'name' => Util::localizedString($g, 'name'),
+ 'quality' => $g['quality'],
+ 'icon' => strToLower($g['icon']),
+ 'enchantment' => $enchantments->getField('name', true),
+ 'jsonequip' => $enchantments->getStatGainForCurrent(),
+ 'colors' => $g['colors'],
+ 'expansion' => $g['expansion'],
+ 'gearscore' => Util::getGemScore($g['itemLevel'], $g['quality'], $g['requiredSkill'] == SKILL_JEWELCRAFTING, $g['itemId'])
+ );
+ }
+
+ $toFile = "var g_gems = ".Util::toJSON($gemsOut).";";
+ $file = 'datasets/'.$loc->json().'/gems';
+
+ if (!CLISetup::writeFile($file, $toFile))
+ $this->success = false;
+ }
+
+ return $this->success;
+ }
+});
+
+?>
diff --git a/setup/tools/filegen/global-js.ss.php b/setup/tools/filegen/global-js.ss.php
new file mode 100644
index 00000000..28fd1d55
--- /dev/null
+++ b/setup/tools/filegen/global-js.ss.php
@@ -0,0 +1,80 @@
+ [[], CLISetup::ARGV_PARAM, 'Compiles the global javascript file (static/js/global.js).']
+ );
+
+ protected $fileTemplateDest = ['static/js/global.js'];
+ protected $fileTemplateSrc = ['global.js'];
+
+ private bool $numFmt = false;
+
+ private function locales() : string
+ {
+ $result = [];
+
+ foreach (CLISetup::$locales as $loc)
+ $result[$loc->value] = array(
+ 'id' => '$LOCALE_' . strtoupper($loc->json()),
+ 'name' => $loc->json(),
+ 'domain' => $loc->domain(),
+ 'description' => $loc->title()
+ );
+
+ return Util::toJSON($result);
+ }
+});
+
+?>
diff --git a/setup/tools/filegen/glyphs.func.php b/setup/tools/filegen/glyphs.func.php
deleted file mode 100644
index f39914d2..00000000
--- a/setup/tools/filegen/glyphs.func.php
+++ /dev/null
@@ -1,91 +0,0 @@
-Select(
- 'SELECT i.id AS itemId,
- i.*,
- IF (g.typeFlags & 0x1, 2, 1) AS type,
- i.subclass AS classs,
- i.requiredLevel AS level,
- s1.id AS glyphSpell,
- ic.name AS icon,
- s1.skillLine1 AS skillId,
- s2.id AS glyphEffect,
- s2.id AS ARRAY_KEY
- FROM ?_items i
- JOIN ?_spell s1 ON s1.id = i.spellid1
- JOIN ?_glyphproperties g ON g.id = s1.effect1MiscValue
- JOIN ?_spell s2 ON s2.id = g.spellId
- JOIN ?_icons ic ON ic.id = s1.iconIdAlt
- WHERE i.classBak = 16');
-
- // check directory-structure
- foreach (Util::$localeStrings as $dir)
- if (!CLISetup::writeDir('datasets/'.$dir))
- $success = false;
-
- $glyphSpells = new SpellList(array(['s.id', array_keys($glyphList)], CFG_SQL_LIMIT_NONE));
-
- foreach (CLISetup::$localeIds as $lId)
- {
- set_time_limit(30);
-
- User::useLocale($lId);
- Lang::load(Util::$localeStrings[$lId]);
-
- $glyphsOut = [];
- foreach ($glyphSpells->iterate() as $__)
- {
- $pop = $glyphList[$glyphSpells->id];
-
- if (!$pop['glyphEffect'])
- continue;
-
- if ($glyphSpells->getField('effect1Id') != 6 && $glyphSpells->getField('effect2Id') != 6 && $glyphSpells->getField('effect3Id') != 6)
- continue;
-
- $glyphsOut[$pop['itemId']] = array(
- 'name' => Util::localizedString($pop, 'name'),
- 'description' => $glyphSpells->parseText()[0],
- 'icon' => $pop['icon'],
- 'type' => $pop['type'],
- 'classs' => $pop['classs'],
- 'skill' => $pop['skillId'],
- 'level' => $pop['level']
- );
- }
-
- $toFile = "var g_glyphs = ".Util::toJSON($glyphsOut).";";
- $file = 'datasets/'.User::$localeString.'/glyphs';
-
- if (!CLISetup::writeFile($file, $toFile))
- $success = false;
- }
-
- return $success;
- }
-?>
diff --git a/setup/tools/filegen/glyphs.ss.php b/setup/tools/filegen/glyphs.ss.php
new file mode 100644
index 00000000..f746276a
--- /dev/null
+++ b/setup/tools/filegen/glyphs.ss.php
@@ -0,0 +1,96 @@
+ [[], CLISetup::ARGV_PARAM, 'Compiles glyphs to file for the talent calculator tool.']
+ );
+
+ protected $setupAfter = [['items', 'spell', 'glyphproperties', 'icons'], []];
+ protected $requiredDirs = ['datasets/'];
+ protected $localized = true;
+
+ public function generate() : bool
+ {
+ $glyphList = DB::Aowow()->selectAssoc(
+ 'SELECT i.`id` AS "itemId",
+ i.*,
+ IF (g.`typeFlags` & 0x1, 2, 1) AS "type",
+ i.`subclass` AS "classs",
+ i.`requiredLevel` AS "level",
+ s1.`id` AS "glyphSpell",
+ ic.`name` AS "icon",
+ s1.`skillLine1` AS "skillId",
+ s2.`id` AS "glyphEffect",
+ s2.`id` AS ARRAY_KEY
+ FROM ::items i
+ JOIN ::spell s1 ON s1.`id` = i.`spellid1`
+ JOIN ::glyphproperties g ON g.`id` = s1.`effect1MiscValue`
+ JOIN ::spell s2 ON s2.`id` = g.`spellId`
+ JOIN ::icons ic ON ic.`id` = s1.`iconIdAlt`
+ WHERE i.classBak = %i',
+ ITEM_CLASS_GLYPH);
+
+ $glyphSpells = new SpellList(array(['s.id', array_keys($glyphList)]));
+
+ foreach (CLISetup::$locales as $loc)
+ {
+ set_time_limit(30);
+
+ Lang::load($loc);
+
+ $glyphsOut = [];
+ foreach ($glyphSpells->iterate() as $__)
+ {
+ $pop = $glyphList[$glyphSpells->id];
+
+ if (!$pop['glyphEffect'])
+ continue;
+
+ if ($glyphSpells->getField('effect1Id') != SPELL_EFFECT_APPLY_AURA && $glyphSpells->getField('effect2Id') != SPELL_EFFECT_APPLY_AURA && $glyphSpells->getField('effect3Id') != SPELL_EFFECT_APPLY_AURA)
+ continue;
+
+ $glyphsOut[$pop['itemId']] = array(
+ 'name' => Util::localizedString($pop, 'name'),
+ 'description' => $glyphSpells->parseText()[0],
+ 'icon' => $pop['icon'],
+ 'type' => $pop['type'],
+ 'classs' => $pop['classs'],
+ 'skill' => $pop['skillId'],
+ 'level' => $pop['level']
+ );
+ }
+
+ $toFile = "var g_glyphs = ".Util::toJSON($glyphsOut).";";
+ $file = 'datasets/'.$loc->json().'/glyphs';
+
+ if (!CLISetup::writeFile($file, $toFile))
+ $this->success = false;
+ }
+
+ return $this->success;
+ }
+});
+
+?>
diff --git a/setup/tools/filegen/img-artwork.ss.php b/setup/tools/filegen/img-artwork.ss.php
new file mode 100644
index 00000000..ff011d0a
--- /dev/null
+++ b/setup/tools/filegen/img-artwork.ss.php
@@ -0,0 +1,132 @@
+ [[], CLISetup::ARGV_PARAM, 'Generate images from /glues/credits (not used on page)'],
+ );
+
+ public $isOptional = true;
+
+ private const TILEORDER = array(
+ 1 => [ [1] ],
+ 2 => [ [1],
+ [2] ],
+ 4 => [ [1, 2],
+ [3, 4] ],
+ 6 => [ [1, 2, 3],
+ [4, 5, 6] ],
+ 8 => [ [1, 2, 3, 4],
+ [5, 6, 7, 8] ],
+ 9 => [ [1, 2, 3],
+ [4, 5, 6],
+ [7, 8, 9] ]
+ );
+
+ // src, resourcePath, localized, [tileOrder], [[dest, destW, destH]]
+ private $genSteps = array(
+ ['Glues/Credits/', null, false, self::TILEORDER, [['cache/Artworks/', 0, 0]]]
+ );
+
+ public function __construct()
+ {
+ $this->imgPath = CLISetup::$srcDir.$this->imgPath;
+ $this->maxExecTime = ini_get('max_execution_time');
+
+ foreach ($this->genSteps[0][self::GEN_IDX_DEST_INFO] as $dir)
+ $this->requiredDirs[] = $dir[0];
+ }
+
+ public function generate() : bool
+ {
+ if (!$this->checkSourceDirs())
+ {
+ CLI::write('one or more source directories are missing:', CLI::LOG_ERROR);
+ $this->success = false;
+ return false;
+ }
+
+ sleep(2);
+
+ [, $realPath, , $tileOrder, $outInfo] = $this->genSteps[0];
+
+ $sum = 0;
+ $imgGroups = [];
+ $files = CLISetup::filesInPath('/'.str_replace('/', '\\/', $realPath).'/i', true);
+ $fileTpl = $outInfo[0][0].'%s.png';
+
+ foreach ($files as $f)
+ {
+ if (preg_match('/([^\/]+)(\d).blp/i', $f, $m))
+ {
+ if (!$m[1] || !$m[2])
+ continue;
+
+ if (!isset($imgGroups[$m[1]]))
+ $imgGroups[$m[1]] = $m[2];
+ else if ($imgGroups[$m[1]] < $m[2])
+ $imgGroups[$m[1]] = $m[2];
+ }
+ }
+
+ // errör-korrekt
+ if (isset($imgGroups['Desolace']))
+ $imgGroups['Desolace'] = 4;
+
+ $total = count($imgGroups);
+
+ CLI::write('Processing '.$total.' files from '.$realPath.' ...');
+
+ foreach ($imgGroups as $name => $fmt)
+ {
+ ini_set('max_execution_time', $this->maxExecTime);
+
+ $sum++;
+ $this->status = ' - '.str_pad($sum.'/'.$total, 8).str_pad('('.number_format($sum * 100 / $total, 2).'%)', 9);
+ $file = sprintf($fileTpl, $name);
+
+ if (!CLISetup::getOpt('force') && file_exists($file))
+ {
+ CLI::write($this->status.' - file '.$file.' was already processed', CLI::LOG_BLANK, true, true);
+ continue;
+ }
+
+ if (!isset($tileOrder[$fmt]))
+ {
+ CLI::write(' - pattern for file '.$name.' not set. skipping', CLI::LOG_WARN);
+ $this->success = false;
+ continue;
+ }
+
+ $order = $tileOrder[$fmt];
+
+ $im = $this->assembleImage($realPath.'/'.$name, $order, count($order[0]) * 256, count($order) * 256);
+ if (!$im)
+ {
+ CLI::write(' - could not assemble file '.$name, CLI::LOG_ERROR);
+ $this->success = false;
+ continue;
+ }
+
+ if (!$this->writeImageFile($im, $file, count($order[0]) * 256, count($order) * 256))
+ $this->success = false;
+ }
+
+ ini_set('max_execution_time', $this->maxExecTime);
+
+ return $this->success;
+ }
+});
+
+?>
diff --git a/setup/tools/filegen/img-maps.ss.php b/setup/tools/filegen/img-maps.ss.php
new file mode 100644
index 00000000..380129cf
--- /dev/null
+++ b/setup/tools/filegen/img-maps.ss.php
@@ -0,0 +1,866 @@
+ [[ ], CLISetup::ARGV_PARAM, 'Generate zone and continental maps and the corresponding \'zones\' datasets.' ],
+/* 1 */ 'spawnmaps' => [['1'], CLISetup::ARGV_OPTIONAL, 'Fallback to generate alpha masks for each zone to match creature and gameobject spawn points.'],
+/* 2 */ 'subzones' => [['2'], CLISetup::ARGV_OPTIONAL, 'Generate additional area maps with highlighting for subzones (optional; skipped by default)' ],
+/* 4 */ 'skip-zones' => [['3'], CLISetup::ARGV_OPTIONAL, 'Prevent default output of zone maps.' ]
+ );
+
+ protected $useGlobalStrings = true;
+ protected $dbcSourceFiles = ['worldmapoverlay', 'worldmaparea', 'dungeonmap'];
+ protected $requiredDirs = ['datasets/'];
+
+ private const M_MAPS = (1 << 0);
+ private const M_SPAWNS = (1 << 1);
+ private const M_SUBZONES = (1 << 2);
+
+ private $modeMask = self::M_SPAWNS | self::M_MAPS;
+
+ private const SPAWNMAP_WH = 1000; // it is square
+ private const MAP_W = 1002;
+ private const MAP_H = 668;
+ private const A_THRESHOLD = 95; // alpha threshold to define subZones: set it too low and you have unspawnable areas inside a zone; set it too high and the border regions overlap
+ private const COLOR_WHITE = [255, 255, 255]; // rgb
+ private const COLOR_BLACK = [ 0, 0, 0]; // rgb
+ private const COLOR_SUBZONE = [ 0, 230, 255, 74]; // rgba - note: rgb is 0-255, a is 0-127
+
+ private const AREA_FLAG_DEFAULT_FLOOR_TERRAIN = 0x004; // Default Dungeon Floor is Terrain
+ private const AREA_FLAG_NO_DEFAULT_FLOOR = 0x100; // Don't use Default Dungeon Floor (typically 1)
+
+ private const CONTINENTS = [0, 1, 530, 571]; // Map.dbc/id
+
+ private const DEST_DIRS = array(
+ ['static/images/wow/maps/%snormal/', 488, 325],
+ ['static/images/wow/maps/%soriginal/', 0, 0], // 1002, 668
+ ['static/images/wow/maps/%ssmall/', 224, 149],
+ ['static/images/wow/maps/%szoom/', 772, 515]
+ );
+
+ private const TILEORDER = array(
+ [1, 2, 3, 4],
+ [5, 6, 7, 8],
+ [9, 10, 11, 12]
+ );
+
+ private const MAP_FILE_PATTERN = '/((\w{4})\/interface\/worldmap(?:\/microdungeon\/([^\/]+))?\/([^\/]+)\/)(\4)(?:(\d{1,2})_)?(\d{1,2})\.(?:blp|png)/i';
+
+ // src, resourcePath, localized, [tileOrder], [[dest, destW, destH]]
+ private $genSteps = array(
+ self::M_MAPS => ['WorldMap/', null, true, self::TILEORDER, self::DEST_DIRS ],
+ self::M_SPAWNS => ['WorldMap/', null, true, self::TILEORDER, [['cache/alphaMaps/', 0, 0]]],
+ self::M_SUBZONES => ['WorldMap/', null, true, self::TILEORDER, self::DEST_DIRS ]
+ );
+
+ private $progress = 0;
+ private $wmOverlays = [];
+ private $dmFloorData = [];
+ private $wmAreas = [];
+ private $multiLevelZones = [];
+ private $mapFiles = []; // [nameINT][floorIdx][loc][tileIdx] => filePath
+ private $microDungeons = [];
+
+ public function __construct()
+ {
+ $this->imgPath = CLISetup::$srcDir.$this->imgPath;
+ $this->maxExecTime = ini_get('max_execution_time');
+
+ // init directories
+ foreach ($this->genSteps as [, , , , $outInfo])
+ {
+ foreach ($outInfo as $dir)
+ {
+ if (strpos($dir[0], '%s') === false)
+ $this->requiredDirs[] = $dir[0];
+ else
+ foreach (CLISetup::$locales as $loc)
+ $this->requiredDirs[] = sprintf($dir[0], $loc->json().DIRECTORY_SEPARATOR);
+ }
+ }
+ }
+
+ public function generate() : bool
+ {
+ // find out what to generate
+ $opts = array_slice(array_keys($this->info), 1);
+ $getO = CLISetup::getOpt(...$opts);
+ $mask = 0x0;
+
+ if ($getO['spawnmaps'])
+ $mask |= self::M_SPAWNS;
+ if ($getO['subzones'])
+ $mask |= self::M_SUBZONES;
+ if (!$getO['skip-zones'])
+ $mask |= self::M_MAPS;
+
+ // unless manually prompted drop spawnmap generation if 90% of spawns have core generated area info
+ $npcPct = DB::World()->selectCell('SELECT SUM(IF(`zoneId` > 0, 1, 0)) / COUNT(*) FROM creature') ?? 0;
+ $goPct = DB::World()->selectCell('SELECT SUM(IF(`zoneId` > 0, 1, 0)) / COUNT(*) FROM gameobject') ?? 0;
+
+ if (!($mask & self::M_SPAWNS) && $npcPct > 0.9 && $goPct > 0.9)
+ $this->modeMask &= ~self::M_SPAWNS;
+
+ $this->modeMask = $mask ?: $this->modeMask;
+
+ if (!$this->modeMask) // why would you do this..?
+ return true;
+
+ // removed unused genSteps
+ foreach ($this->genSteps as $idx => $_)
+ if (!($idx & $this->modeMask))
+ unset($this->genSteps[$idx]);
+
+ if (!$this->checkSourceDirs())
+ {
+ CLI::write('[img-maps] One or more source directories are missing.', CLI::LOG_ERROR);
+ $this->success = false;
+ return false;
+ }
+
+ sleep(2);
+
+ if ($this->prepare())
+ {
+ $this->buildMaps();
+ $this->buildZonesFile();
+ }
+
+ return $this->success;
+ }
+
+ private function buildMapsFUTURE() : void
+ {
+ $sumFloors = array_sum(array_column($this->dmFloorData, 1));
+ $sumAreas = count($this->wmAreas);
+ $sumMaps = count(CLISetup::$locales) * ($sumAreas + $sumFloors);
+
+ CLI::write('[img-maps] Processing '.$sumAreas.' zone maps and '.$sumFloors.' dungeon maps from Interface/WorldMap/ for locale: '.Lang::concat(CLISetup::$locales, callback: fn($x) => $x->name));
+
+ /* todo: retrain brain and generate maps by given files and GlobalStrings. Then assign dbc data to them not the other way round like it is now.
+ foreach ($this->mapFiles as $name => [$floors, $isMultilevel])
+ {
+ // skip redundant data of a microDungeons
+ if (in_array($name, $this->microDungeons))
+ continue;
+
+ $this->wmAreas = $this->wmAreas[$name] ?? [];
+ if (!$this->wmAreas)
+ {
+ CLI::write('[img-maps] no WMA data for map file '.CLI::bold($name), CLI::LOG_WARN);
+ continue;
+ }
+
+ $wmaId = $this->wmAreas['id'];
+ $zoneId = $this->wmAreas['areaId'];
+ $mapId = $this->wmAreas['mapId'];
+ $flags = $this->wmAreas['flags'] ?? 0; // flags added in 4.x
+
+ if ($isMultilevel)
+ $this->multiLevelZones[$zoneId] = [];
+
+ // TODO
+ // - Ahn'Kahet (4494) has a secondary map file, that is not referenced in DungeonMap.dbc but looks nice. Lets manually reference it.
+ // if (isset($floorData[4494]))
+ // $floorData[4494][1] = 2;
+ if ($zoneId == 206)
+ var_dump($floors);
+
+
+ foreach ($floors as $locId => [$floorData, $basePath])
+ {
+ ksort($floorData);
+
+ $resOverlay = null;
+ if (!$isMultilevel)
+ $resOverlay = $this->generateOverlay($wmaId, $name, $basePath);
+
+ // create spawn-maps if wanted
+ if ($resOverlay && $this->modeMask & self::M_SPAWNS)
+ {
+ $outFile = $this->genSteps[self::M_SPAWNS][self::GEN_IDX_DEST_INFO][0][0] . $zoneId . '.png';
+ if (!$this->buildSpawnMap($resOverlay, $outFile))
+ $this->success = false;
+ }
+
+ foreach ($floorData as $floorIdx => $tileData)
+ {
+ $outFile = $zoneId;
+
+ // naming of the base floor file is a bit wonky. unsure when the -0 suffix should be implicit or explicit
+ // just note, that the floor names from GlobalStrings.lua always have a '0' as base level suffix
+
+ if (!$floorIdx && $isMultilevel && !($flags & self::AREA_FLAG_DEFAULT_FLOOR_TERRAIN))
+ CLI::write('[img-maps] zone '.$name.' is multilevel and has base level map file, but is not flagged for use', CLI::LOG_INFO);
+
+ if ($isMultilevel && !$floorIdx)
+ {
+ if (in_array($mapId, self::CONTINENTS))
+ $outFile .= '-0';
+ else if ($this->wmAreas['defaultDungeonMapId'] < 0)
+ $outFile .= '-0';
+ // else
+ // implicit -0
+ }
+ else if ($isMultilevel)
+ $outFile .= '-'.$floorIdx;
+
+ if ($isMultilevel)
+ $this->multiLevelZones[$zoneId][$floorIdx] = $outFile;
+
+
+ foreach ($tileData as $tileIdx => $filePath)
+ {
+
+ }
+ }
+ }
+
+ if ($isMultilevel)
+ $this->multiLevelZones[$zoneId] = array_values($this->multiLevelZones[$zoneId]);
+
+ if ($this->modeMask & self::M_SUBZONES)
+ {
+ // get subzones for mapFile from wmaData and apply overlays
+ }
+ }
+ */
+
+ $progressLoc = -1;
+ foreach (CLISetup::$locales as $l => $loc)
+ {
+ $progressLoc++;
+
+ // source for mapFiles
+ $mapSrcDir = '';
+ if ($this->modeMask & self::M_SPAWNS)
+ $mapSrcDir = $this->genSteps[self::M_SPAWNS][1][$l] ?? '';
+ if (!$mapSrcDir && $this->modeMask & self::M_SUBZONES)
+ $mapSrcDir = $this->genSteps[self::M_SUBZONES][1][$l] ?? '';
+ if (!$mapSrcDir)
+ $mapSrcDir = $this->genSteps[self::M_MAPS][1][$l] ?? '';
+ if (!$mapSrcDir)
+ {
+ $this->success = false;
+ CLI::write(' - no suitable localized map files found for locale '.$l, CLI::LOG_ERROR);
+ continue;
+ }
+
+ foreach ($this->wmAreas as $progressArea => $areaEntry)
+ {
+ $curMap = $progressArea + count($this->wmAreas) * $progressLoc;
+ $this->status = ' - ' . str_pad($curMap.'/'.($sumMaps), 10) . str_pad('('.number_format($curMap * 100 / $sumMaps, 2).'%)', 9);
+
+ $wmaId = $areaEntry['id'];
+ $zoneId = $areaEntry['areaId'];
+ $mapId = $areaEntry['mapId'];
+ $textureStr = $areaEntry['nameINT'];
+ $flags = $areaEntry['flags'] ?? 0; // flags added in 4.x
+
+ [$floorStr, $nFloors] = $this->dmFloorData[in_array($mapId, self::CONTINENTS) ? -$wmaId : $mapId] ?? ['', 0];
+
+ if ($nFloors && !isset($this->multiLevelZones))
+ $this->multiLevelZones[$zoneId] = [];
+
+ CLI::write(
+ str_pad('['.$areaEntry['areaId'].']', 7) .
+ str_pad($areaEntry['nameINT'], 22) .
+ str_pad('Overlays: '.count($this->wmOverlays[$areaEntry['id']] ?? []), 14) .
+ str_pad('Dungeon Maps: '.($nFloors + ((($flags ?? 0) & self::AREA_FLAG_DEFAULT_FLOOR_TERRAIN) ? 1 : 0)), 18)
+ );
+
+ $srcPath = $mapSrcDir.DIRECTORY_SEPARATOR.$textureStr;
+ if (!CLISetup::fileExists($srcPath))
+ {
+ $this->success = false;
+ CLI::write('worldmap file '.$srcPath.' missing for selected locale '.$loc->name, CLI::LOG_ERROR);
+ continue;
+ }
+
+ $resOverlay = null;
+
+ // zone has overlays (is in open world; is not multilevel)
+ if (isset($this->wmOverlays[$wmaId]))
+ {
+ $resOverlay = $this->generateOverlay($wmaId, $srcPath);
+
+ // create spawn-maps if wanted
+ if ($this->modeMask & self::M_SPAWNS)
+ $this->buildSpawnMap($resOverlay, $zoneId);
+ }
+
+ if (!($this->modeMask & self::M_MAPS))
+ continue;
+
+ // check, if the current zone is multiLeveled
+ $floors = [0];
+ if ($floorStr)
+ $floors = array_merge($floors, explode(' ', $floorStr));
+
+ // - Ahn'Kahet (4494) has a secondary map file, that is not referenced in DungeonMap.dbc but looks nice. Lets manually reference it.
+ if ($zoneId == 4494)
+ $floors[] = 2;
+
+ $resMap = null;
+ foreach ($floors as $floorIdx)
+ {
+ ini_set('max_execution_time', $this->maxExecTime);
+
+ $file = $srcPath.DIRECTORY_SEPARATOR.$textureStr;
+
+ // todo: Dalaran [4395] has no level 0 but is not skipped here
+ if (!$floorIdx && !($flags & self::AREA_FLAG_DEFAULT_FLOOR_TERRAIN) && !in_array($mapId, self::CONTINENTS))
+ continue;
+
+ if ($nFloors && ($floorIdx || $flags & self::AREA_FLAG_DEFAULT_FLOOR_TERRAIN))
+ $this->multiLevelZones[$zoneId][$floorIdx] = $zoneId . '-' . $floorIdx;
+
+ if ($floorIdx)
+ $file .= $floorIdx . '_';
+
+ $doSkip = 0x0;
+ $outFile = [];
+
+ foreach (self::DEST_DIRS as $sizeIdx => [$path, $width, $height])
+ {
+ $outFile[$sizeIdx] = sprintf($path, $loc->json().DIRECTORY_SEPARATOR) . $zoneId;
+
+ /* dataset 'zones' requires that ...
+ * 3959 - Black Temple: starts with empty floor suffix
+ * 4075 - Sunwell: starts with empty floor suffix
+ * 4723 - Map 650 CoT: 5-man reuses raid map (649) but only the upper floor. Check DungeonMap.dbc
+ */
+
+ if ($nFloors && ($floorIdx || $flags & self::AREA_FLAG_DEFAULT_FLOOR_TERRAIN))
+ $outFile[$sizeIdx] .= '-'.$floorIdx;
+
+ $outFile[$sizeIdx] .= '.jpg';
+
+ if (!CLISetup::getOpt('force') && file_exists($outFile[$sizeIdx]))
+ {
+ CLI::write($this->status.' - file '.$outFile[$sizeIdx].' was already processed', CLI::LOG_BLANK, true, true);
+ $doSkip |= (1 << $sizeIdx);
+ }
+ }
+
+ if ($doSkip == 0xF)
+ continue;
+
+ $resMap = $this->assembleImage($file, self::TILEORDER, self::MAP_W, self::MAP_H);
+ if (!$resMap)
+ {
+ CLI::write(' - could not create image resource for zone '.$zoneId.($nFloors ? ' floor '.$floorIdx : ''), CLI::LOG_ERROR);
+ $this->success = false;
+ continue;
+ }
+
+ if ($resOverlay && !$floorIdx)
+ {
+ imagecopymerge($resMap, $resOverlay, 0, 0, 0, 0, imagesx($resOverlay), imagesy($resOverlay), 100);
+ imagedestroy($resOverlay);
+ }
+
+ // create map
+ if ($this->modeMask & self::M_MAPS)
+ {
+ foreach (self::DEST_DIRS as $sizeIdx => [, $width, $height])
+ {
+ if ($doSkip & (1 << $sizeIdx))
+ continue;
+
+ if (!$this->writeImageFile($resMap, $outFile[$sizeIdx], $width ?: self::MAP_W, $height ?: self::MAP_H))
+ $this->success = false;
+ }
+ }
+ }
+
+ // also create subzone-maps
+ if ($resMap && isset($this->wmOverlays[$wmaId]) && $this->modeMask & self::M_SUBZONES)
+ $this->buildSubZones($resMap, $wmaId, $loc);
+
+ if ($resMap)
+ imagedestroy($resMap);
+
+ // this takes a while; ping mysql just in case
+ DB::Aowow()->selectCell('SELECT 1');
+ }
+ }
+ }
+
+ private function prepare() : bool
+ {
+ $this->wmOverlays = DB::Aowow()->selectAssoc('SELECT *, `worldMapAreaId` AS ARRAY_KEY, `id` AS ARRAY_KEY2 FROM dbc_worldmapoverlay WHERE `textureString` <> ""');
+ $this->wmAreas = DB::Aowow()->selectAssoc('SELECT `id`, `mapId`, `areaId`, UPPER(`nameINT`) AS `nameINT`, IF(`areaId`, `areaId`, -`id`) AS ARRAY_KEY FROM dbc_worldmaparea');
+ $this->dmFloorData = DB::Aowow()->selectAssoc('SELECT IF(`mapId` IN %in, -`worldMapAreaId`, `mapId`) AS ARRAY_KEY, GROUP_CONCAT(DISTINCT `floor` SEPARATOR " ") AS "0", COUNT(DISTINCT `floor`) AS "1" FROM dbc_dungeonmap WHERE `worldMapAreaId` <> 0 GROUP BY ARRAY_KEY', self::CONTINENTS);
+ if (!$this->wmOverlays || !$this->wmAreas || !$this->dmFloorData)
+ {
+ CLI::write('[img-maps] - could not read required dbc files: WorldMapArea.dbc ['.count($this->wmAreas ?: []).' entries]; WorldMapOverlay.dbc ['.count($this->wmOverlays ?: []).'] entries; DungeonMap.dbc ['.count($this->dmFloorData ?: []).' entries]', CLI::LOG_ERROR);
+ $this->success = false;
+ return false;
+ }
+
+ // DM fixups...
+ // unpack + sort floors
+ array_walk($this->dmFloorData, function (&$x) { $x[0] = explode(' ', $x[0]); sort($x[0]); });
+
+ // move Dalaran from Howling Fjord to .. well .. Dalaran
+ $this->dmFloorData[-4395] = $this->dmFloorData[-495];
+ unset($this->dmFloorData[-495]);
+
+ // "custom" - show second level of Ahn'Kahet not shown but present in-game
+ $this->dmFloorData[619][0][] = 2;
+ $this->dmFloorData[619][1]++;
+
+ // WMA fixups...
+ foreach ($this->wmAreas as &$a)
+ {
+ // flags added in 4.x but required for 3.3.5. Where are they? Derived from defaultDungeonMapId (also refered to as defaultDungeonFloor) being < 0 ?
+ // no idea, hardcode this shit
+ switch ($a['areaId'])
+ { // i deem the missing '-0' a mistake > v < this will not be perpetuated
+ case 4273: // Ulduar > base + 5 > 4273: ['4273-0', '4273-1', '4273-2', '4273-3', '4273-4', '4273-5']
+ case 4075: // SunwellPlateau > base + 1 > 4075: ['4075', '4075-1'],
+ case 3959: // BlackTemple > base + 7 > 3959: ['3959', '3959-1', '3959-2', '3959-3', '3959-4', '3959-5', '3959-6', '3959-7'],
+ $a['flags'] = self::AREA_FLAG_DEFAULT_FLOOR_TERRAIN;
+ break;
+ case 4100: // CoTStratholme > base + 1 > 4100: ['4100-1', '4100-2'],
+ $a['flags'] = self::AREA_FLAG_DEFAULT_FLOOR_TERRAIN | self::AREA_FLAG_NO_DEFAULT_FLOOR;
+ break;
+ default:
+ $a['flags'] = $a['flags'] ?? 0; // flags added in 4.x
+ }
+
+ if ($a['areaId'])
+ continue;
+
+ switch ($a['id'])
+ {
+ case 13: $a['areaId'] = -6; break; // Kalimdor
+ case 14: $a['areaId'] = -3; break; // Eastern Kingdoms
+ case 466: $a['areaId'] = -2; break; // Outland
+ case 485: $a['areaId'] = -5; break; // Northrend
+ }
+ }
+ $this->wmAreas[-1] = ['id' => -1, 'areaId' => -1, 'flags' => 0x0, 'mapId' => 0, 'nameINT' => 'World'];
+ $this->wmAreas[-4] = ['id' => -4, 'areaId' => -4, 'flags' => 0x0, 'mapId' => 0, 'nameINT' => 'Cosmic'];
+
+ ksort($this->wmAreas); // just so we can sift through the log more easily
+
+ /*
+ i should be walking through interface/worldmap first and THEN check the worldmaparea / dungeonmap from the file pattern
+ floorIdx is optional and per map. (e.g. continents share their floors and yes continents can have dungeon maps)
+
+ > /interface/worldmap//(_).blp
+ > /interface/worldmap/microdungeon///(_).blp
+
+ microdungeons (5.x+?) may be redundant with regluar map files.
+
+ e.g.:
+ > enGB/interface/worldmap/microdungeon/durotar/burningbladecoven/burningbladecoven8_12.blp
+
+ from nameInt "durotar" we get wmaId = 4, areaTableId = 14 and mapId = 1 (floorIdx = 8 from file string)
+ with mapId and floor (and wmaId) we get the coordinates from dungeonmap.dbc
+
+ thus the map file name is: -.png > 14-8.png
+ and the floor is named: DUNGEON_FLOOR_ > DUNGEON_FLOOR_DUROTAR8 (Aquelarre del Filo Ardiente) *nyak nyak nyak*
+
+
+ note: some map file may have no floorIdx but the tileIdx is still separated by an underscore. Those files should be ignored.
+
+ */
+ /* FUTURE
+ foreach (CLISetup::filesInPath(self::MAP_FILE_PATTERN, true) as $file)
+ {
+ if (!preg_match(self::MAP_FILE_PATTERN, $file, $m))
+ continue;
+
+ [, $basePath, $locStr, $mdParent, $nameINT, $nameINT, $floorIdx, $tileIdx] = $m;
+
+ $loc = CLISetup::$expectedPaths[strtolower(substr($locStr, 0, 2)).strtoupper(substr($locStr, 2))] ?? LOCALE_EN;
+
+ if ($mdParent)
+ $this->microDungeons[] = strtolower($nameINT);
+
+ $key = strtolower($mdParent ?: $nameINT);
+
+ $this->mapFiles[$key][0][$loc][0][$floorIdx ?: 0][$tileIdx] = $file;
+ $this->mapFiles[$key][0][$loc][1] = $basePath;
+ $this->mapFiles[$key][1] = ($this->mapFiles[$key][1] ?? false) ?: (($floorIdx ?: 0) > 1);
+ }
+ */
+
+ return true;
+ }
+
+ private function buildMaps() : void
+ {
+ $sumFloors = array_sum(array_column($this->dmFloorData, 1));
+ $sumAreas = count($this->wmAreas);
+ $sumMaps = count(CLISetup::$locales) * ($sumAreas + $sumFloors);
+
+ CLI::write('[img-maps] Processing '.$sumAreas.' zone maps and '.$sumFloors.' dungeon maps from Interface/WorldMap/ for locale: '.Lang::concat(CLISetup::$locales, callback: fn($x) => CLI::bold($x->name)));
+
+ foreach (CLISetup::$locales as $l => $loc)
+ {
+ // source for mapFiles
+ $mapSrcDir = '';
+ if ($this->modeMask & self::M_SPAWNS)
+ $mapSrcDir = $this->genSteps[self::M_SPAWNS][1][$l] ?? '';
+ if (!$mapSrcDir && $this->modeMask & self::M_SUBZONES)
+ $mapSrcDir = $this->genSteps[self::M_SUBZONES][1][$l] ?? '';
+ if (!$mapSrcDir)
+ $mapSrcDir = $this->genSteps[self::M_MAPS][1][$l] ?? '';
+ if (!$mapSrcDir)
+ {
+ CLI::write('[img-maps] - No suitable localized map files found for locale '.CLI::bold($loc->name).'.', CLI::LOG_ERROR);
+ $this->success = false;
+ continue;
+ }
+
+ foreach ($this->wmAreas as $areaEntry)
+ {
+ $resOverlay = null;
+ $resMap = null;
+
+ $wmaId = $areaEntry['id'];
+ $zoneId = $areaEntry['areaId'];
+ $mapId = $areaEntry['mapId'];
+ $textureStr = $areaEntry['nameINT'];
+ $flags = $areaEntry['flags'];
+
+ [$dmFloors, $nFloors] = $this->dmFloorData[in_array($mapId, self::CONTINENTS) ? -$zoneId : $mapId] ?? [[0], 0];
+
+ $this->progress += ($nFloors ?: 1) + ($flags & self::AREA_FLAG_DEFAULT_FLOOR_TERRAIN ? 1 : 0);
+ $this->status = ' - ' . str_pad($this->progress.'/'.($sumMaps), 10) . str_pad('('.number_format($this->progress * 100 / $sumMaps, 2).'%)', 9);
+
+ // includes base level...
+ if ($flags & self::AREA_FLAG_DEFAULT_FLOOR_TERRAIN)
+ {
+ array_unshift($dmFloors, 0); // 0 => 0, 1 => 1, etc.
+ $nFloors++;
+
+ // .. which is not set in dbc 0 => 1, 1 => 2, etc.
+ if ($flags & self::AREA_FLAG_NO_DEFAULT_FLOOR)
+ $dmFloors = array_combine($dmFloors, array_map(fn($x) => ++$x, $dmFloors));
+ }
+ else if ($dmFloors != [0]) // 1 => 1, 2 => 2, etc.
+ $dmFloors = array_combine($dmFloors, $dmFloors);
+
+ CLI::write(
+ '['.$loc->json().'] ' .
+ str_pad('['.$areaEntry['areaId'].']', 7) .
+ str_pad($areaEntry['nameINT'], 22) .
+ str_pad('Overlays: '.count($this->wmOverlays[$areaEntry['id']] ?? []), 14) .
+ str_pad('Dungeon Maps: '.$nFloors, 18)
+ );
+
+ $srcPath = $mapSrcDir.DIRECTORY_SEPARATOR.$textureStr;
+ if (!CLISetup::fileExists($srcPath))
+ {
+ CLI::write('[img-maps] - WorldMap file path '.$srcPath.' missing for selected locale '.CLI::bold($loc->name), CLI::LOG_ERROR);
+ $this->success = false;
+ continue;
+ }
+
+ $srcPath .= DIRECTORY_SEPARATOR;
+
+ // zone has overlays (is in open world; is not multilevel)
+ if (isset($this->wmOverlays[$wmaId]) && ($this->modeMask & (self::M_MAPS | self::M_SPAWNS | self::M_SUBZONES)))
+ {
+ $resOverlay = $this->generateOverlay($wmaId, $srcPath);
+
+ // create spawn-maps if wanted
+ if ($resOverlay && ($this->modeMask & self::M_SPAWNS))
+ $this->buildSpawnMap($resOverlay, $zoneId);
+ }
+
+ // check if we can create base map anyway
+ $png = $srcPath.$textureStr.'1.png';
+ $blp = $srcPath.$textureStr.'1.blp';
+ $hasBaseMap = CLISetup::fileExists($blp) || CLISetup::fileExists($png);
+
+ foreach ($dmFloors as $srcFloorIdx => $outFloorIdx)
+ {
+ ini_set('max_execution_time', $this->maxExecTime);
+
+ $doSkip = 0x0;
+ $outPaths = [];
+ $srcFile = $srcPath.$textureStr;
+ $outFile = $zoneId;
+
+ if (!$srcFloorIdx && !$hasBaseMap)
+ {
+ CLI::write('[img-maps] - Zone has no base floor, but is referenced with base floor in dmFloors.', CLI::LOG_WARN);
+ continue;
+ }
+
+ if ($srcFloorIdx)
+ $srcFile .= $srcFloorIdx.'_';
+
+ if ($nFloors > 1)
+ if ($outFloorIdx || $flags & self::AREA_FLAG_DEFAULT_FLOOR_TERRAIN)
+ $outFile .= '-'.$outFloorIdx;
+
+ if ($nFloors > 1)
+ $this->multiLevelZones[$zoneId][$outFile] = $outFile;
+
+ if (!($this->modeMask & (self::M_MAPS | self::M_SUBZONES)))
+ continue;
+
+ foreach (self::DEST_DIRS as $sizeIdx => [$path, $width, $height])
+ {
+ $outPaths[$sizeIdx] = sprintf($path, strtolower($loc->json()).DIRECTORY_SEPARATOR) . $outFile . '.jpg';
+
+ if (!CLISetup::getOpt('force') && file_exists($outPaths[$sizeIdx]))
+ {
+ CLI::write($this->status.' - file '.$outPaths[$sizeIdx].' was already processed', CLI::LOG_BLANK, true, true);
+ $doSkip |= (1 << $sizeIdx);
+ }
+ }
+
+ // can't skip map creation if we are to generate subzones later. although they may already exist and get skipped anyway *shrug*
+ if ($doSkip == 0xF && !($this->modeMask & self::M_SUBZONES))
+ continue;
+
+ $resMap = $this->assembleImage($srcFile, self::TILEORDER, self::MAP_W, self::MAP_H);
+ if (!$resMap)
+ {
+ CLI::write('[img-maps] - Could not create image resource for '.($nFloors ? 'floor '.$srcFloorIdx : 'base level'), CLI::LOG_ERROR);
+ $this->success = false;
+ continue;
+ }
+
+ if ($resOverlay && !$nFloors)
+ {
+ imagecopymerge($resMap, $resOverlay, 0, 0, 0, 0, imagesx($resOverlay), imagesy($resOverlay), 100);
+ imagedestroy($resOverlay);
+ }
+
+ // create map
+ if ($this->modeMask & self::M_MAPS)
+ {
+ foreach (self::DEST_DIRS as $sizeIdx => [, $width, $height])
+ {
+ if ($doSkip & (1 << $sizeIdx))
+ continue;
+
+ if (!$this->writeImageFile($resMap, $outPaths[$sizeIdx], $width ?: self::MAP_W, $height ?: self::MAP_H))
+ $this->success = false;
+ }
+ }
+ }
+
+ // also create subzone-maps
+ if ($resMap && isset($this->wmOverlays[$wmaId]) && $this->modeMask & self::M_SUBZONES)
+ $this->buildSubZones($resMap, $wmaId, $loc);
+
+ if ($resMap)
+ imagedestroy($resMap);
+
+ // this takes a while; ping mysql just in case
+ DB::Aowow()->selectCell('SELECT 1');
+ }
+ }
+ }
+
+ private function buildZonesFile() : void
+ {
+ $areaNames = array_combine(
+ array_column($this->wmAreas, 'areaId'),
+ array_map(fn($x) => strtoupper($x), array_column($this->wmAreas, 'nameINT'))
+ );
+
+ if ($this->multiLevelZones)
+ {
+ ksort($this->multiLevelZones);
+ $this->multiLevelZones = array_map('array_values', $this->multiLevelZones);
+ }
+ else
+ {
+ CLI::write('[img-maps] No data fetched from either WorldMapArea.dbc or DungeonMap.dbc. Multilevel zones will not display.', CLI::LOG_ERROR);
+ $this->success = false;
+ }
+
+ $zoneAreas = [];
+ // careful: nameINT may end in a number and have > 9 floors attached. see: KARAZHAN17, ULDUAR771
+ foreach (CLISetup::searchGlobalStrings('/^DUNGEON_FLOOR_([a-z_]+(?:\d\d)?)(\d{1,2})\s=\s\"(.+)\";$/i') as $lId => [$_, $nameINT, $floor, $nameLOC])
+ {
+ // yes, multiple zones can point to the same map files
+ if ($zoneIds = array_keys($areaNames, $nameINT))
+ {
+ foreach ($zoneIds as $zId)
+ if (isset($this->multiLevelZones[$zId]))
+ $zoneAreas[$lId][$zId][$floor] = $nameLOC;
+ }
+ else
+ CLI::write('[img-maps] ['.$nameINT.'] from GlobalStrings.lua not found in WorldMapArea.dbc', CLI::LOG_WARN);
+ }
+
+ foreach (CLISetup::$locales as $lId => $loc)
+ {
+ Lang::load($loc);
+
+ // "custom" - show second level of Ahn'Kahet not shown but present in-game
+ if (isset($zoneAreas[$lId][4494]))
+ $zoneAreas[$lId][4494][2] = Lang::maps('floorN', [2]);
+
+ foreach ($zoneAreas[$lId] as $zoneId => $floorData)
+ {
+ $nStrings = count($floorData);
+ $nFloors = count($this->multiLevelZones[$zoneId] ?? []);
+ if ($nStrings == $nFloors)
+ continue;
+
+ // todo: just note for now, try to compensate later?
+ CLI::write('[img-maps] ['.$loc->json().'] '.str_pad('['.$zoneId.']', 7).'floor count mismatch between GlobalStrings: '.$nStrings.' and image files: '.$nFloors, CLI::LOG_WARN);
+ }
+
+ ksort($zoneAreas[$lId]);
+
+ $zoneAreas[$lId] = array_map('array_values', $zoneAreas[$lId]);
+
+ // don't convert numbers to int in json
+ $toFile = "Mapper.multiLevelZones = ".Util::toJSON($this->multiLevelZones, 0x0).";\n\n";
+ $toFile .= "var g_zone_areas = ".Util::toJSON($zoneAreas[$lId]).";";
+ $file = 'datasets/'.$loc->json().'/zones';
+
+ if (!CLISetup::writeFile($file, $toFile))
+ $this->success = false;
+ }
+ }
+
+ private function buildSpawnMap(\GdImage $resOverlay, int $zoneId) : void
+ {
+ $outFile = $this->genSteps[self::M_SPAWNS][self::GEN_IDX_DEST_INFO][0][0] . $zoneId . '.png';
+
+ if (!CLISetup::getOpt('force') && file_exists($outFile))
+ {
+ CLI::write($this->status.' - file '.$outFile.' was already processed', CLI::LOG_BLANK, true, true);
+ return;
+ }
+
+ $tmp = imagecreate(self::SPAWNMAP_WH, self::SPAWNMAP_WH);
+ $cbg = imagecolorallocate($tmp, ...self::COLOR_WHITE);
+ $cfg = imagecolorallocate($tmp, ...self::COLOR_BLACK);
+
+ for ($y = 0; $y < self::SPAWNMAP_WH; $y++)
+ {
+ for ($x = 0; $x < self::SPAWNMAP_WH; $x++)
+ {
+ $a = imagecolorat($resOverlay, ($x * self::MAP_W) / self::SPAWNMAP_WH, ($y * self::MAP_H) / self::SPAWNMAP_WH) >> 24;
+ imagesetpixel($tmp, $x, $y, $a < self::A_THRESHOLD ? $cfg : $cbg);
+ }
+ }
+
+ imagecolordeallocate($tmp, $cbg);
+ imagecolordeallocate($tmp, $cfg);
+
+ if (!$this->writeImageFile($tmp, $outFile, self::SPAWNMAP_WH, self::SPAWNMAP_WH))
+ $this->success = false;
+ }
+
+ private function buildSubZones(\GdImage $resMap, int $wmaId, Locale $loc) : void
+ {
+ foreach ($this->wmOverlays[$wmaId] as &$row)
+ {
+ $doSkip = 0x0;
+ $outFile = [];
+
+ foreach (self::DEST_DIRS as $sizeIdx => [$path, , ])
+ {
+ $outFile[$sizeIdx] = sprintf($path, $loc->json() . DIRECTORY_SEPARATOR) . $row['areaTableId'].'.jpg';
+ if (!CLISetup::getOpt('force') && file_exists($outFile[$sizeIdx]))
+ {
+ CLI::write($this->status.' - file '.$outFile[$sizeIdx].' was already processed', CLI::LOG_BLANK, true, true);
+ $doSkip |= (1 << $sizeIdx);
+ }
+ }
+
+ if ($doSkip == 0xF)
+ continue;
+
+ $subZone = imagecreatetruecolor(self::MAP_W, self::MAP_H);
+ imagecopy($subZone, $resMap, 0, 0, 0, 0, imagesx($resMap), imagesy($resMap));
+ imagecopy($subZone, $row['maskimage'], $row['x'], $row['y'], 0, 0, imagesx($row['maskimage']), imagesy($row['maskimage']));
+
+ foreach (self::DEST_DIRS as $sizeIdx => [, $width, $height])
+ {
+ if ($doSkip & (1 << $sizeIdx))
+ continue;
+
+ if (!$this->writeImageFile($subZone, $outFile[$sizeIdx], $width ?: self::MAP_W, $height ?: self::MAP_H))
+ $this->success = false;
+ }
+
+ imagedestroy($subZone);
+ }
+ }
+
+ private function generateOverlay(int $wmaId, string $basePath) : ?\GdImage
+ {
+ if (!isset($this->wmOverlays[$wmaId]))
+ return null;
+
+ $resOverlay = $this->createAlphaImage(self::MAP_W, self::MAP_H);
+
+ foreach ($this->wmOverlays[$wmaId] as &$row)
+ {
+ $i = 1;
+ $y = 0;
+ while ($y < $row['h'])
+ {
+ $x = 0;
+ while ($x < $row['w'])
+ {
+ $img = $this->loadImageFile($basePath . $row['textureString'] . $i, $noSrcFile);
+ if (!$img)
+ {
+ if ($noSrcFile)
+ CLI::write('[img-maps] - overlay tile ' . $basePath . $row['textureString'] . $i . '.blp missing.', CLI::LOG_ERROR);
+
+ break 2;
+ }
+
+ imagecopy($resOverlay, $img, $row['x'] + $x, $row['y'] + $y, 0, 0, imagesx($img), imagesy($img));
+
+ // prepare subzone image
+ if ($this->modeMask & self::M_SUBZONES)
+ {
+ if (!isset($row['maskimage']))
+ {
+ $row['maskimage'] = $this->createAlphaImage($row['w'], $row['h']);
+ $row['maskcolor'] = imagecolorallocatealpha($row['maskimage'], ...self::COLOR_SUBZONE);
+ }
+
+ for ($my = 0; $my < imagesy($img); $my++)
+ for ($mx = 0; $mx < imagesx($img); $mx++)
+ if ((imagecolorat($img, $mx, $my) >> 24) < self::A_THRESHOLD)
+ imagesetpixel($row['maskimage'], $x + $mx, $y + $my, $row['maskcolor']);
+ }
+
+ imagedestroy($img);
+
+ $x += 256;
+ $i++;
+ }
+ $y += 256;
+ }
+ }
+
+ return $resOverlay;
+ }
+});
+
+?>
diff --git a/setup/tools/filegen/img-talentcalc.ss.php b/setup/tools/filegen/img-talentcalc.ss.php
new file mode 100644
index 00000000..c32221c5
--- /dev/null
+++ b/setup/tools/filegen/img-talentcalc.ss.php
@@ -0,0 +1,112 @@
+ [[], CLISetup::ARGV_PARAM, 'Generate backgrounds for the talent calculator.'],
+ );
+
+ protected $dbcSourceFiles = ['talenttab', 'chrclasses'];
+
+ private const DEST_DIRS = array(
+ ['static/images/wow/hunterpettalents/', 0, 0],
+ ['static/images/wow/talents/backgrounds/', 0, 0]
+ );
+
+ private const TILEORDER = array(
+ ['-TopLeft', '-TopRight'],
+ ['-BottomLeft', '-BottomRight']
+ );
+
+ // src, resourcePath, localized, [tileOrder], [[dest, destW, destH]]
+ private $genSteps = array(
+ ['TalentFrame/', null, false, self::TILEORDER, self::DEST_DIRS]
+ );
+
+ public function __construct()
+ {
+ $this->imgPath = CLISetup::$srcDir.$this->imgPath;
+ $this->maxExecTime = ini_get('max_execution_time');
+
+ // init directories
+ foreach (self::DEST_DIRS as $dir)
+ $this->requiredDirs[] = $dir[0];
+ }
+
+ public function generate() : bool
+ {
+ if (!$this->checkSourceDirs())
+ {
+ CLI::write('one or more source directories are missing:', CLI::LOG_ERROR);
+ $this->success = false;
+ return false;
+ }
+
+ sleep(2);
+
+ $tTabs = DB::Aowow()->selectAssoc('SELECT tt.`creatureFamilyMask`, tt.`textureFile`, tt.`tabNumber`, cc.`fileString` FROM dbc_talenttab tt LEFT JOIN dbc_chrclasses cc ON cc.`id` = IF(tt.`classMask`, LOG(2, tt.`classMask`) + 1, 0)');
+ if (!$tTabs)
+ {
+ CLI::write(' - TalentTab.dbc or ChrClasses.dbc is empty...?', CLI::LOG_ERROR);
+ $this->success = false;
+ return false;
+ }
+
+ $sum = 0;
+ $total = count($tTabs);
+ [, $realPath, , $tileOrder, $outInfo] = $this->genSteps[0];
+
+ CLI::write('Processing '.$total.' files from '.$realPath.' ...');
+ foreach ($tTabs as $tt)
+ {
+ ini_set('max_execution_time', $this->maxExecTime);
+ $sum++;
+ $this->status = ' - '.str_pad($sum.'/'.$total, 8).str_pad('('.number_format($sum * 100 / $total, 2).'%)', 9);
+
+ if ($tt['creatureFamilyMask']) // is PetCalc
+ {
+ $size = [244, 364];
+ $outFile = sprintf($outInfo[0][0].'bg_%d.jpg', log($tt['creatureFamilyMask'], 2) + 1);
+ }
+ else
+ {
+ $size = [204, 554];
+ $outFile = sprintf($outInfo[1][0].'%s_%d.jpg', strtolower($tt['fileString']), $tt['tabNumber'] + 1);
+ }
+
+ if (!CLISetup::getOpt('force') && file_exists($outFile))
+ {
+ CLI::write($this->status.' - file '.$outFile.' was already processed', CLI::LOG_BLANK, true, true);
+ continue;
+ }
+
+ $im = $this->assembleImage($realPath.'/'.$tt['textureFile'], $tileOrder, 256 + 44, 256 + 75);
+ if (!$im)
+ {
+ CLI::write(' - could not assemble file '.$tt['textureFile'], CLI::LOG_ERROR);
+ $this->success = false;
+ continue;
+ }
+
+ if (!$this->writeImageFile($im, $outFile, $size[0], $size[1]))
+ $this->success = false;
+ }
+
+ ini_set('max_execution_time', $this->maxExecTime);
+
+ return $this->success;
+ }
+});
+
+?>
diff --git a/setup/tools/filegen/itemScaling.func.php b/setup/tools/filegen/itemScaling.func.php
deleted file mode 100644
index cbca24f9..00000000
--- a/setup/tools/filegen/itemScaling.func.php
+++ /dev/null
@@ -1,115 +0,0 @@
- $row)
- {
- foreach ($row as &$r)
- $r = str_pad($r, 5, " ", STR_PAD_LEFT);
-
- $buff[] = str_pad($id, 7, " ", STR_PAD_LEFT).": [".implode(', ', $row)."]";
- }
-
- return "{\r\n".implode(",\r\n", $buff)."\r\n}";
- }
-
- function itemScalingRB()
- {
- $ratings = array(
- 12 => 1, // ITEM_MOD_DEFENSE_SKILL_RATING => CR_DEFENSE_SKILL
- 13 => 2, // ITEM_MOD_DODGE_RATING => CR_DODGE
- 14 => 3, // ITEM_MOD_PARRY_RATING => CR_PARRY
- 15 => 4, // ITEM_MOD_BLOCK_RATING => CR_BLOCK
- 16 => 5, // ITEM_MOD_HIT_MELEE_RATING => CR_HIT_MELEE
- 17 => 6, // ITEM_MOD_HIT_RANGED_RATING => CR_HIT_RANGED
- 18 => 7, // ITEM_MOD_HIT_SPELL_RATING => CR_HIT_SPELL
- 19 => 8, // ITEM_MOD_CRIT_MELEE_RATING => CR_CRIT_MELEE
- 20 => 9, // ITEM_MOD_CRIT_RANGED_RATING => CR_CRIT_RANGED
- 21 => 10, // ITEM_MOD_CRIT_SPELL_RATING => CR_CRIT_SPELL
- 22 => 11, // ITEM_MOD_HIT_TAKEN_MELEE_RATING => CR_HIT_TAKEN_MELEE
- 23 => 12, // ITEM_MOD_HIT_TAKEN_RANGED_RATING => CR_HIT_TAKEN_RANGED
- 24 => 13, // ITEM_MOD_HIT_TAKEN_SPELL_RATING => CR_HIT_TAKEN_SPELL
- 25 => 14, // ITEM_MOD_CRIT_TAKEN_MELEE_RATING => CR_CRIT_TAKEN_MELEE [may be forced 0]
- 26 => 15, // ITEM_MOD_CRIT_TAKEN_RANGED_RATING => CR_CRIT_TAKEN_RANGED [may be forced 0]
- 27 => 16, // ITEM_MOD_CRIT_TAKEN_SPELL_RATING => CR_CRIT_TAKEN_SPELL [may be forced 0]
- 28 => 17, // ITEM_MOD_HASTE_MELEE_RATING => CR_HASTE_MELEE
- 29 => 18, // ITEM_MOD_HASTE_RANGED_RATING => CR_HASTE_RANGED
- 30 => 19, // ITEM_MOD_HASTE_SPELL_RATING => CR_HASTE_SPELL
- 31 => 5, // ITEM_MOD_HIT_RATING => [backRef]
- 32 => 8, // ITEM_MOD_CRIT_RATING => [backRef]
- 33 => 11, // ITEM_MOD_HIT_TAKEN_RATING => [backRef] [may be forced 0]
- 34 => 14, // ITEM_MOD_CRIT_TAKEN_RATING => [backRef] [may be forced 0]
- 35 => 14, // ITEM_MOD_RESILIENCE_RATING => [backRef]
- 36 => 17, // ITEM_MOD_HASTE_RATING => [backRef]
- 37 => 23, // ITEM_MOD_EXPERTISE_RATING => CR_EXPERTISE
- 44 => 24 // ITEM_MOD_ARMOR_PENETRATION_RATING => CR_ARMOR_PENETRATION
- );
-
- $data = $ratings;
-
- $offsets = array_map(function ($v) { // LookupEntry(cr*GT_MAX_LEVEL+level-1)
- return $v * 100 + 60 - 1;
- }, $ratings);
- $base = DB::Aowow()->selectCol('SELECT CAST((idx + 1 - 60) / 100 AS UNSIGNED) AS ARRAY_KEY, ratio FROM dbc_gtcombatratings WHERE idx IN (?a)', $offsets);
-
- $offsets = array_map(function ($v) { // LookupEntry((getClass()-1)*GT_MAX_RATING+cr+1)
- return (CLASS_WARRIOR - 1) * 32 + $v + 1;
- }, $ratings);
- $mods = DB::Aowow()->selectCol('SELECT idx - 1 AS ARRAY_KEY, ratio FROM dbc_gtoctclasscombatratingscalar WHERE idx IN (?a)', $offsets);
-
- foreach ($data as $itemMod => &$val)
- $val = CFG_DEBUG ? $base[$val].' / '.$mods[$val] : $base[$val] / $mods[$val];
-
- if (!CFG_DEBUG)
- return Util::toJSON($data);
-
- $buff = [];
- foreach ($data as $k => $v)
- $buff[] = $k.': '.$v;
-
- return "{\r\n ".implode(",\r\n ", $buff)."\r\n}";
- }
-
- function itemScalingSV()
- {
- /* so the javascript expects a slightly different structure, than the dbc provides .. f*** it
- e.g.
- dbc - 80: 97 97 56 41 210 395 878 570 120 156 86 112 108 220 343 131 73 140 280 527 1171 2093
- expected - 80: 97 97 56 131 41 210 395 878 1570 120 156 86 112 108 220 343 0 0 73 140 280 527 1171 2093
- */
- $fields = Util::$ssdMaskFields;
- array_walk($fields, function(&$v, $k) {
- $v = $v ?: '0 AS idx'.$k; // NULL => 0 (plus some index so we can have 2x 0)
- });
-
- $data = DB::Aowow()->select('SELECT id AS ARRAY_KEY, '.implode(', ', $fields).' FROM dbc_scalingstatvalues');
- foreach ($data as &$d)
- $d = array_values($d); // strip indizes
-
- return CFG_DEBUG ? debugify($data) : Util::toJSON($data);
- }
-
- function itemScalingSD()
- {
- $data = DB::Aowow()->select('SELECT *, id AS ARRAY_KEY FROM dbc_scalingstatdistribution');
- foreach ($data as &$row)
- {
- $row = array_values($row);
- array_splice($row, 0, 1);
- }
-
- return CFG_DEBUG ? debugify($data) : Util::toJSON($data);
- }
-
-?>
diff --git a/setup/tools/filegen/itemscaling.ss.php b/setup/tools/filegen/itemscaling.ss.php
new file mode 100644
index 00000000..3ddf0b3a
--- /dev/null
+++ b/setup/tools/filegen/itemscaling.ss.php
@@ -0,0 +1,138 @@
+ [[], CLISetup::ARGV_PARAM, 'Compiles item scaling data to file to make heirloom tooltips interactive.']
+ );
+
+ protected $fileTemplateDest = ['datasets/item-scaling'];
+ protected $fileTemplateSrc = ['item-scaling.in'];
+
+ protected $dbcSourceFiles = ['scalingstatdistribution', 'scalingstatvalues', 'gtoctclasscombatratingscalar', 'gtcombatratings'];
+
+ private function debugify(array $data) : string
+ {
+ $buff = [];
+ foreach ($data as $id => $row)
+ {
+ foreach ($row as &$r)
+ $r = str_pad($r, 5, " ", STR_PAD_LEFT);
+
+ $buff[] = str_pad($id, 7, " ", STR_PAD_LEFT).": [".implode(', ', $row)."]";
+ }
+
+ return "{\r\n".implode(",\r\n", $buff)."\r\n}";
+ }
+
+ private function itemScalingRB() : string
+ {
+ // data and format observed via wayback machine. Not entirely sure about the redunacy within the combat ratings though.
+ $ratings = array(
+ Stat::DEFENSE_RTG => CR_DEFENSE_SKILL,
+ Stat::DODGE_RTG => CR_DODGE,
+ Stat::PARRY_RTG => CR_PARRY,
+ Stat::BLOCK_RTG => CR_BLOCK,
+ Stat::MELEE_HIT_RTG => CR_HIT_MELEE,
+ Stat::RANGED_HIT_RTG => CR_HIT_RANGED,
+ Stat::SPELL_HIT_RTG => CR_HIT_SPELL,
+ Stat::MELEE_CRIT_RTG => CR_CRIT_MELEE,
+ Stat::RANGED_CRIT_RTG => CR_CRIT_RANGED,
+ Stat::SPELL_CRIT_RTG => CR_CRIT_SPELL,
+ Stat::MELEE_HIT_TAKEN_RTG => CR_HIT_TAKEN_MELEE,
+ Stat::RANGED_HIT_TAKEN_RTG => CR_HIT_TAKEN_RANGED,
+ Stat::SPELL_HIT_TAKEN_RTG => CR_HIT_TAKEN_SPELL,
+ Stat::MELEE_CRIT_TAKEN_RTG => CR_CRIT_TAKEN_MELEE, // may be forced 0
+ Stat::RANGED_CRIT_TAKEN_RTG => CR_CRIT_TAKEN_RANGED, // may be forced 0
+ Stat::SPELL_CRIT_TAKEN_RTG => CR_CRIT_TAKEN_SPELL, // may be forced 0
+ Stat::MELEE_HASTE_RTG => CR_HASTE_MELEE,
+ Stat::RANGED_HASTE_RTG => CR_HASTE_RANGED,
+ Stat::SPELL_HASTE_RTG => CR_HASTE_SPELL,
+ Stat::HIT_RTG => CR_HIT_MELEE,
+ Stat::CRIT_RTG => CR_CRIT_MELEE,
+ Stat::HIT_TAKEN_RTG => CR_HIT_TAKEN_MELEE, // may be forced 0
+ Stat::CRIT_TAKEN_RTG => CR_CRIT_TAKEN_MELEE, // may be forced 0
+ Stat::RESILIENCE_RTG => CR_CRIT_TAKEN_MELEE,
+ Stat::HASTE_RTG => CR_HASTE_MELEE,
+ Stat::EXPERTISE_RTG => CR_EXPERTISE,
+ Stat::ARMOR_PENETRATION_RTG => CR_ARMOR_PENETRATION
+ );
+
+ $data = $ratings;
+
+ $offsets = array_map(function ($v) { // LookupEntry(cr*GT_MAX_LEVEL+level-1)
+ return $v * 100 + 60 - 1; // combat rating where introduced during the transition vanilla > burnig crusade. So at level 60 (at the time) the rating on the item was equal to 1% effect and is still the baseline in 3.3.5a.
+ }, $ratings);
+ $base = DB::Aowow()->selectCol('SELECT CAST((idx + 1 - 60) / 100 AS UNSIGNED) AS ARRAY_KEY, ratio FROM dbc_gtcombatratings WHERE idx IN %in', $offsets);
+
+ /* non-1 scaler in 3.3.5.12340
+ | ratingId | classId | ratio |
+ | 17 | 2 | 1.3 |
+ | 17 | 6 | 1.3 |
+ | 17 | 7 | 1.3 |
+ | 17 | 11 | 1.3 |
+ | 24 | < all > | 1.1 |
+ */
+
+ $offsets = array_map(function ($v) { // LookupEntry((getClass()-1)*GT_MAX_RATING+cr+1)
+ return (ChrClass::WARRIOR->value - 1) * 32 + $v + 1; // should this be dynamic per pinned character? ITEM_MOD HASTE has a worse scaler for a subset of classes (see table)
+ }, $ratings);
+ $mods = DB::Aowow()->selectCol('SELECT idx - 1 AS ARRAY_KEY, ratio FROM dbc_gtoctclasscombatratingscalar WHERE idx IN %in', $offsets);
+
+ foreach ($data as &$val)
+ $val = Cfg::get('DEBUG') ? $base[$val].' / '.$mods[$val] : $base[$val] / $mods[$val];
+
+ if (!Cfg::get('DEBUG'))
+ return Util::toJSON($data);
+
+ $buff = [];
+ foreach ($data as $k => $v)
+ $buff[] = $k.': '.$v;
+
+ return "{\r\n ".implode(",\r\n ", $buff)."\r\n}";
+ }
+
+ private function itemScalingSV() : string
+ {
+ /* so the javascript expects a slightly different structure, than the dbc provides .. f*** it
+ e.g.
+ dbc - 80: 97 97 56 41 210 395 878 570 120 156 86 112 108 220 343 131 73 140 280 527 1171 2093
+ expected - 80: 97 97 56 131 41 210 395 878 1570 120 156 86 112 108 220 343 0 0 73 140 280 527 1171 2093
+ */
+ $fields = Util::$ssdMaskFields;
+ array_walk($fields, function(&$v, $k) {
+ $v = $v ?: '0 AS idx'.$k; // NULL => 0 (plus some index so we can have 2x 0)
+ });
+
+ $data = DB::Aowow()->selectAssoc('SELECT id AS ARRAY_KEY, '.implode(', ', $fields).' FROM dbc_scalingstatvalues');
+ foreach ($data as &$d)
+ $d = array_values($d); // strip indizes
+
+ return Cfg::get('DEBUG') ? $this->debugify($data) : Util::toJSON($data);
+ }
+
+ private function itemScalingSD() : string
+ {
+ $data = DB::Aowow()->selectAssoc('SELECT *, id AS ARRAY_KEY FROM dbc_scalingstatdistribution');
+ foreach ($data as &$row)
+ {
+ $row = array_values($row);
+ array_splice($row, 0, 1);
+ }
+
+ return Cfg::get('DEBUG') ? $this->debugify($data) : Util::toJSON($data);
+ }
+})
+
+?>
diff --git a/setup/tools/filegen/itemsets.func.php b/setup/tools/filegen/itemsets.func.php
deleted file mode 100644
index 36c1b24c..00000000
--- a/setup/tools/filegen/itemsets.func.php
+++ /dev/null
@@ -1,131 +0,0 @@
-Select('SELECT * FROM ?_itemset ORDER BY refSetId DESC');
- $jsonBonus = [];
-
- // check directory-structure
- foreach (Util::$localeStrings as $dir)
- if (!CLISetup::writeDir('datasets/'.$dir))
- $success = false;
-
- foreach (CLISetup::$localeIds as $lId)
- {
- User::useLocale($lId);
- Lang::load(Util::$localeStrings[$lId]);
-
- $itemsetOut = [];
- foreach ($setList as $set)
- {
- set_time_limit(15);
-
- $setOut = array(
- 'id' => $set['id'],
- 'idbak' => $set['refSetId'],
- 'name' => (7 - $set['quality']).Util::jsEscape(Util::localizedString($set, 'name')),
- 'pieces' => [],
- 'heroic' => !!$set['heroic'], // should be bool
- 'maxlevel' => $set['maxLevel'],
- 'minlevel' => $set['minLevel'],
- 'type' => $set['type'],
- 'setbonus' => []
- );
-
- if ($set['classMask'])
- {
- $setOut['reqclass'] = $set['classMask'];
- $setOut['classes'] = [];
-
- for ($i = 0; $i < 12; $i++)
- if ($set['classMask'] & (1 << $i))
- $setOut['classes'][] = $i + 1;
- }
-
- if ($set['contentGroup'])
- $setOut['note'] = $set['contentGroup'];
-
- for ($i = 1; $i < 11; $i++)
- if ($set['item'.$i])
- $setOut['pieces'][] = $set['item'.$i];
-
- for ($i = 1; $i < 9; $i++)
- {
- if (!$set['bonus'.$i] || !$set['spell'.$i])
- continue;
-
- // costy and locale-independant -> cache
- if (!isset($jsonBonus[$set['spell'.$i]]))
- $jsonBonus[$set['spell'.$i]] = (new SpellList(array(['s.id', (int)$set['spell'.$i]])))->getStatGain()[$set['spell'.$i]];
-
- if (!isset($setOut['setbonus'][$set['bonus'.$i]]))
- $setOut['setbonus'][$set['bonus'.$i]] = $jsonBonus[$set['spell'.$i]];
- else
- Util::arraySumByKey($setOut['setbonus'][$set['bonus'.$i]], $jsonBonus[$set['spell'.$i]]);
- }
-
- foreach ($setOut['setbonus'] as $k => $v)
- {
- if (empty($v))
- unset($setOut['setbonus'][$k]);
- else
- {
- foreach ($v as $sk => $sv)
- {
- if ($str = Game::$itemMods[$sk])
- {
- $setOut['setbonus'][$k][$str] = $sv;
- unset($setOut['setbonus'][$k][$sk]);
- }
- }
- }
- }
-
- if (empty($setOut['setbonus']))
- unset($setOut['setbonus']);
-
- $itemsetOut[$setOut['id']] = $setOut;
- }
-
- $toFile = "var g_itemsets = ".Util::toJSON($itemsetOut).";";
- $file = 'datasets/'.User::$localeString.'/itemsets';
-
- if (!CLISetup::writeFile($file, $toFile))
- $success = false;
- }
-
- return $success;
- }
-?>
diff --git a/setup/tools/filegen/itemsets.ss.php b/setup/tools/filegen/itemsets.ss.php
new file mode 100644
index 00000000..3b367648
--- /dev/null
+++ b/setup/tools/filegen/itemsets.ss.php
@@ -0,0 +1,130 @@
+ [[], CLISetup::ARGV_PARAM, 'Compiles available item sets used throughout the page to file.']
+ );
+
+ protected $setupAfter = [['itemset', 'spell'], []];
+ protected $requiredDirs = ['datasets/'];
+ protected $localized = true;
+
+ public function generate() : bool
+ {
+ $setList = DB::Aowow()->selectAssoc('SELECT * FROM ::itemset ORDER BY `refSetId` DESC');
+ $jsonBonus = [];
+
+ foreach (CLISetup::$locales as $loc)
+ {
+ Lang::load($loc);
+
+ $itemsetOut = [];
+ foreach ($setList as $set)
+ {
+ set_time_limit(15);
+
+ $setOut = array(
+ 'id' => $set['id'],
+ 'idbak' => $set['refSetId'],
+ 'name' => (ITEM_QUALITY_HEIRLOOM - $set['quality']).Util::jsEscape(Util::localizedString($set, 'name')),
+ 'pieces' => [],
+ 'heroic' => !!$set['heroic'], // should be bool
+ 'maxlevel' => $set['maxLevel'],
+ 'minlevel' => $set['minLevel'],
+ 'type' => $set['type'],
+ 'setbonus' => []
+ );
+
+ if ($set['classMask'])
+ {
+ $setOut['reqclass'] = $set['classMask'];
+ $setOut['classes'] = ChrClass::fromMask($set['classMask']);
+ }
+
+ if ($set['contentGroup'])
+ $setOut['note'] = $set['contentGroup'];
+
+ for ($i = 1; $i < 11; $i++)
+ if ($set['item'.$i])
+ $setOut['pieces'][] = $set['item'.$i];
+
+ $_spells = [];
+ for ($i = 1; $i < 9; $i++)
+ {
+ if (!$set['bonus'.$i] || isset($jsonBonus[$set['spell'.$i]]))
+ continue;
+
+ $_spells[] = $set['spell'.$i];
+ }
+
+ // costy and locale-independent -> cache
+ if ($_spells)
+ $jsonBonus += (new SpellList(array(['s.id', $_spells])))->getStatGain();
+
+ $setbonus = [];
+ for ($i = 1; $i < 9; $i++)
+ {
+ $itemQty = $set['bonus'.$i];
+ $itemSpl = $set['spell'.$i];
+ if (!$itemQty || !$itemSpl || !isset($jsonBonus[$itemSpl]))
+ continue;
+
+ if ($x = $jsonBonus[$itemSpl]->toJson(Stat::FLAG_ITEM, false))
+ {
+ if (!isset($setbonus[$itemQty]))
+ $setbonus[$itemQty] = [];
+
+ Util::arraySumByKey($setbonus[$itemQty], $x);
+ }
+ }
+
+ if ($setbonus)
+ $setOut['setbonus'] = $setbonus;
+
+ $itemsetOut[$setOut['id']] = $setOut;
+ }
+
+ $toFile = "var g_itemsets = ".Util::toJSON($itemsetOut).";";
+ $file = 'datasets/'.$loc->json().'/itemsets';
+
+ if (!CLISetup::writeFile($file, $toFile))
+ $this->success = false;
+ }
+
+ return $this->success;
+ }
+});
+
+?>
diff --git a/setup/tools/filegen/locales.func.php b/setup/tools/filegen/locales.func.php
deleted file mode 100644
index abb9de21..00000000
--- a/setup/tools/filegen/locales.func.php
+++ /dev/null
@@ -1,62 +0,0 @@
- " 0: { // English\r\n" .
- " id: LOCALE_ENUS,\r\n" .
- " name: 'enus',\r\n" .
- " domain: 'www',\r\n" .
- " description: 'English'\r\n" .
- " }",
- LOCALE_FR => " 2: { // French\r\n" .
- " id: LOCALE_FRFR,\r\n" .
- " name: 'frfr',\r\n" .
- " domain: 'fr',\r\n" .
- " description: 'Fran' + String.fromCharCode(231) + 'ais'\r\n" .
- " }",
- LOCALE_DE => " 3: { // German\r\n" .
- " id: LOCALE_DEDE,\r\n" .
- " name: 'dede',\r\n" .
- " domain: 'de',\r\n" .
- " description: 'Deutsch'\r\n" .
- " }",
- LOCALE_CN => " 4:{ // Chinese\r\n" .
- " id: LOCALE_ZHCN,\r\n" .
- " name: 'zhcn',\r\n" .
- " domain: 'cn',\r\n" .
- " description: String.fromCharCode(31616, 20307, 20013, 25991)\r\n" .
- " }",
- LOCALE_ES => " 6: { // Spanish\r\n" .
- " id: LOCALE_ESES,\r\n" .
- " name: 'eses',\r\n" .
- " domain: 'es',\r\n" .
- " description: 'Espa' + String.fromCharCode(241) + 'ol'\r\n" .
- " }",
- LOCALE_RU => " 8: { // Russian\r\n" .
- " id: LOCALE_RURU,\r\n" .
- " name: 'ruru',\r\n" .
- " domain: 'ru',\r\n" .
- " description: String.fromCharCode(1056, 1091, 1089, 1089, 1082, 1080, 1081)\r\n" .
- " }",
- );
-
- foreach (CLISetup::$localeIds as $l)
- if (isset($available[$l]))
- $result[] = $available[$l];
-
- return implode(",\r\n", $result);
- }
-
-?>
diff --git a/setup/tools/filegen/pets.func.php b/setup/tools/filegen/pets.func.php
deleted file mode 100644
index ee2bb476..00000000
--- a/setup/tools/filegen/pets.func.php
+++ /dev/null
@@ -1,97 +0,0 @@
-Select(
- 'SELECT cr.id,
- cr.name_loc0, cr.name_loc2, cr.name_loc3, cr.name_loc4, cr.name_loc6, cr.name_loc8,
- cr.minLevel,
- cr.maxLevel,
- ft.A,
- ft.H,
- cr.rank AS classification,
- cr.family,
- cr.displayId1 AS displayId,
- cr.textureString AS skin,
- LOWER(SUBSTRING_INDEX(cf.iconString, "\\\\", -1)) AS icon,
- cf.petTalentType AS type
- FROM ?_creature cr
- JOIN ?_factiontemplate ft ON ft.id = cr.faction
- JOIN dbc_creaturefamily cf ON cf.id = cr.family
- WHERE cr.typeFlags & 0x1 AND (cr.cuFlags & 0x2) = 0
- ORDER BY cr.id ASC');
-
- // check directory-structure
- foreach (Util::$localeStrings as $dir)
- if (!CLISetup::writeDir('datasets/'.$dir))
- $success = false;
-
- foreach (CLISetup::$localeIds as $lId)
- {
- User::useLocale($lId);
- Lang::load(Util::$localeStrings[$lId]);
-
- $petsOut = [];
- foreach ($petList as $pet)
- {
- // get locations
- // again: caching will save you time and nerves
- if (!isset($locations[$pet['id']]))
- $locations[$pet['id']] = DB::Aowow()->SelectCol('SELECT DISTINCT areaId FROM ?_spawns WHERE type = ?d AND typeId = ?d', Type::NPC, $pet['id']);
-
- $petsOut[$pet['id']] = array(
- 'id' => $pet['id'],
- 'name' => Util::localizedString($pet, 'name'),
- 'minlevel' => $pet['minLevel'],
- 'maxlevel' => $pet['maxLevel'],
- 'location' => $locations[$pet['id']],
- 'react' => [$pet['A'], $pet['H']],
- 'classification' => $pet['classification'],
- 'family' => $pet['family'],
- 'displayId' => $pet['displayId'],
- 'skin' => $pet['skin'],
- 'icon' => $pet['icon'],
- 'type' => $pet['type']
- );
- }
-
- $toFile = "var g_pets = ".Util::toJSON($petsOut).";";
- $file = 'datasets/'.User::$localeString.'/pets';
-
- if (!CLISetup::writeFile($file, $toFile))
- $success = false;
- }
-
- return $success;
- }
-?>
diff --git a/setup/tools/filegen/pets.ss.php b/setup/tools/filegen/pets.ss.php
new file mode 100644
index 00000000..d5f223e4
--- /dev/null
+++ b/setup/tools/filegen/pets.ss.php
@@ -0,0 +1,98 @@
+ [[], CLISetup::ARGV_PARAM, 'Compiles tameable hunter pets to file for the talent calculator tool.']
+ );
+
+ protected $dbcSourceFiles = ['creaturefamily'];
+ protected $setupAfter = [['creature', 'factions', 'spawns'], []];
+ protected $requiredDirs = ['datasets/'];
+ protected $localized = true;
+
+ public function generate() : bool
+ {
+ $petList = DB::Aowow()->selectAssoc(
+ 'SELECT cr.`id`,
+ cr.`name_loc0`, cr.`name_loc2`, cr.`name_loc3`, cr.`name_loc4`, cr.`name_loc6`, cr.`name_loc8`,
+ cr.`minLevel`, cr.`maxLevel`,
+ ft.`A`, ft.`H`,
+ cr.`rank` AS "classification",
+ cr.`family`,
+ cr.`displayId1` AS "displayId",
+ cr.`textureString` AS "skin",
+ LOWER(SUBSTRING_INDEX(cf.`iconString`, "\\", -1)) AS "icon",
+ cf.`petTalentType` AS "type"
+ FROM ::creature cr
+ JOIN ::factiontemplate ft ON ft.`id` = cr.`faction`
+ JOIN dbc_creaturefamily cf ON cf.`id` = cr.`family`
+ WHERE cr.`typeFlags` & 0x1 AND (cr.`cuFlags` & %i) = 0
+ ORDER BY cr.`id` ASC',
+ NPC_CU_DIFFICULTY_DUMMY
+ );
+
+ $locations = DB::Aowow()->selectCol('SELECT `typeId` AS ARRAY_KEY, `areaId` AS ARRAY_KEY2, `areaId` FROM ::spawns WHERE `type` = %i AND `typeId` IN %in GROUP BY `typeId`, `areaId`', Type::NPC, array_column($petList, 'id'));
+
+ foreach (CLISetup::$locales as $loc)
+ {
+ Lang::load($loc);
+
+ $petsOut = [];
+ foreach ($petList as $pet)
+ {
+ $petsOut[$pet['id']] = array(
+ 'id' => $pet['id'],
+ 'name' => Util::localizedString($pet, 'name'),
+ 'minlevel' => $pet['minLevel'],
+ 'maxlevel' => $pet['maxLevel'],
+ 'location' => $locations[$pet['id']] ?? [],
+ 'react' => [$pet['A'], $pet['H']],
+ 'classification' => $pet['classification'],
+ 'family' => $pet['family'],
+ 'displayId' => $pet['displayId'],
+ 'skin' => $pet['skin'],
+ 'icon' => $pet['icon'],
+ 'type' => $pet['type']
+ );
+ }
+
+ $toFile = "var g_pets = ".Util::toJSON($petsOut).";";
+ $file = 'datasets/'.$loc->json().'/pets';
+
+ if (!CLISetup::writeFile($file, $toFile))
+ $this->success = false;
+ }
+
+ return $this->success;
+ }
+});
+
+?>
diff --git a/setup/tools/filegen/profiler.func.php b/setup/tools/filegen/profiler.func.php
deleted file mode 100644
index 5ca1d526..00000000
--- a/setup/tools/filegen/profiler.func.php
+++ /dev/null
@@ -1,413 +0,0 @@
- $type, 'typeId' => $typeId, 'groups' => $groups, 'comment' => $comment];
- else
- {
- $exclusions[$k]['groups'] |= $groups;
- if ($comment)
- $exclusions[$k]['comment'] .= '; '.$comment;
- }
- };
-
-
- /**********/
- /* Quests */
- /**********/
- $scripts[] = function() use ($exAdd)
- {
- $success = true;
- $condition = [
- CFG_SQL_LIMIT_NONE,
- 'AND',
- [['cuFlags', CUSTOM_EXCLUDE_FOR_LISTVIEW], 0],
- [['flags', QUEST_FLAG_DAILY | QUEST_FLAG_WEEKLY | QUEST_FLAG_REPEATABLE | QUEST_FLAG_AUTO_REWARDED, '&'], 0],
- [['specialFlags', QUEST_FLAG_SPECIAL_REPEATABLE | QUEST_FLAG_SPECIAL_DUNGEON_FINDER | QUEST_FLAG_SPECIAL_MONTHLY, '&'], 0]
- ];
- $questz = new QuestList($condition);
-
- // get quests for exclusion
- foreach ($questz->iterate() as $id => $__)
- {
- switch ($questz->getField('reqSkillId'))
- {
- case 356:
- $exAdd(Type::QUEST, $id, PR_EXCLUDE_GROUP_REQ_FISHING);
- break;
- case 202:
- $exAdd(Type::QUEST, $id, PR_EXCLUDE_GROUP_REQ_ENGINEERING);
- break;
- case 197:
- $exAdd(Type::QUEST, $id, PR_EXCLUDE_GROUP_REQ_TAILORING);
- break;
- }
- }
-
- $_ = [];
- $currencies = array_column($questz->rewards, Type::CURRENCY);
- foreach ($currencies as $curr)
- foreach ($curr as $cId => $qty)
- $_[] = $cId;
-
- $relCurr = new CurrencyList(array(['id', $_]));
-
- foreach (CLISetup::$localeIds as $l)
- {
- set_time_limit(20);
-
- User::useLocale($l);
- Lang::load(Util::$localeStrings[$l]);
-
- $buff = "var _ = g_gatheredcurrencies;\n";
- foreach ($relCurr->getListviewData() as $id => $data)
- $buff .= '_['.$id.'] = '.Util::toJSON($data).";\n";
-
- $buff .= "\n\nvar _ = g_quests;\n";
- foreach ($questz->getListviewData() as $id => $data)
- $buff .= '_['.$id.'] = '.Util::toJSON($data).";\n";
-
- $buff .= "\ng_quest_catorder = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10];\n";
-
- if (!CLISetup::writeFile('datasets/'.User::$localeString.'/p-quests', $buff))
- $success = false;
- }
-
- return $success;
- };
-
- /**********/
- /* Titles */
- /**********/
- $scripts[] = function() use ($exAdd)
- {
- $success = true;
- $condition = array(
- CFG_SQL_LIMIT_NONE,
- [['cuFlags', CUSTOM_EXCLUDE_FOR_LISTVIEW, '&'], 0],
- );
- $titlez = new TitleList($condition);
-
- // get titles for exclusion
- foreach ($titlez->iterate() as $id => $__)
- if (empty($titlez->sources[$id][4]) && empty($titlez->sources[$id][12]))
- $exAdd(Type::TITLE, $id, PR_EXCLUDE_GROUP_UNAVAILABLE);
-
- foreach (CLISetup::$localeIds as $l)
- {
- set_time_limit(5);
-
- User::useLocale($l);
- Lang::load(Util::$localeStrings[$l]);
-
- foreach ([0, 1] as $g) // gender
- {
- $buff = "var _ = g_titles;\n";
- foreach ($titlez->getListviewData() as $id => $data)
- {
- $data['name'] = Util::localizedString($titlez->getEntry($id), $g ? 'female' : 'male');
- unset($data['namefemale']);
- $buff .= '_['.$id.'] = '.Util::toJSON($data).";\n";
- }
-
- if (!CLISetup::writeFile('datasets/'.User::$localeString.'/p-titles-'.$g, $buff))
- $success = false;
- }
- }
-
- return $success;
- };
-
- /**********/
- /* Mounts */
- /**********/
- $scripts[] = function() use ($exAdd)
- {
- $success = true;
- $condition = array(
- CFG_SQL_LIMIT_NONE,
- [['cuFlags', CUSTOM_EXCLUDE_FOR_LISTVIEW, '&'], 0],
- ['typeCat', -5],
- ['castTime', 0, '!']
- );
- $mountz = new SpellList($condition);
-
- // we COULD go into aowow_sources to get the faction of the source and apply it to the spell. .. Or we could keep our sanity and assume TC did nothing wrong. haHA! no!
- $factionSet = DB::World()->selectCol('SELECT alliance_id AS ARRAY_KEY, horde_id FROM player_factionchange_spells WHERE alliance_id IN (?a) OR horde_id IN (?a)', $mountz->getFoundIDs(), $mountz->getFoundIDs());
- $conditionSet = DB::World()->selectCol('SELECT SourceEntry AS ARRAY_KEY, ConditionValue1 FROM conditions WHERE SourceTypeOrReferenceId = ?d AND ConditionTypeOrReference = ?d AND SourceEntry IN (?a)', CND_SRC_SPELL, CND_SKILL, $mountz->getFoundIDs());
-
- // get mounts for exclusion
- foreach ($conditionSet as $mount => $skill)
- {
- if ($skill == 202)
- $exAdd(Type::SPELL, $mount, PR_EXCLUDE_GROUP_REQ_ENGINEERING);
- else if ($skill == 197)
- $exAdd(Type::SPELL, $mount, PR_EXCLUDE_GROUP_REQ_TAILORING);
- }
-
- foreach (CLISetup::$localeIds as $l)
- {
- set_time_limit(5);
-
- User::useLocale($l);
- Lang::load(Util::$localeStrings[$l]);
-
- $buff = "var _ = g_spells;\n";
- foreach ($mountz->getListviewData(ITEMINFO_MODEL) as $id => $data)
- {
- // two cases where the spell is unrestricted but the castitem has class restriction (too lazy to formulate ruleset)
- if ($id == 66906) // Argent Charger
- $data['reqclass'] = CLASS_PALADIN;
- else if ($id == 54729) // Winged Steed of the Ebon Blade
- $data['reqclass'] = CLASS_DEATHKNIGHT;
-
- if (isset($factionSet[$id])) // alliance owned
- $data['side'] = SIDE_ALLIANCE;
- else if (in_array($id, $factionSet)) // horde owned
- $data['side'] = SIDE_HORDE;
- else
- $data['side'] = SIDE_BOTH;
-
- rsort($data['skill']); // riding (777) expected at pos 0
-
- $data['quality'] = $data['name'][0];
- $data['name'] = mb_substr($data['name'], 1);
- $buff .= '_['.$id.'] = '.Util::toJSON($data).";\n";
- }
-
- if (!CLISetup::writeFile('datasets/'.User::$localeString.'/p-mounts', $buff))
- $success = false;
- }
-
- return $success;
- };
-
- /**************/
- /* Companions */
- /**************/
- $scripts[] = function() use ($exAdd)
- {
- $success = true;
- $condition = array(
- CFG_SQL_LIMIT_NONE,
- [['cuFlags', CUSTOM_EXCLUDE_FOR_LISTVIEW, '&'], 0],
- ['typeCat', -6]
- );
- $companionz = new SpellList($condition);
-
- foreach (CLISetup::$localeIds as $l)
- {
- set_time_limit(5);
-
- User::useLocale($l);
- Lang::load(Util::$localeStrings[$l]);
-
- $buff = "var _ = g_spells;\n";
- foreach ($companionz->getListviewData(ITEMINFO_MODEL) as $id => $data)
- {
- $data['quality'] = $data['name'][0];
- $data['name'] = mb_substr($data['name'], 1);
- $buff .= '_['.$id.'] = '.Util::toJSON($data).";\n";
- }
-
- if (!CLISetup::writeFile('datasets/'.User::$localeString.'/p-companions', $buff))
- $success = false;
- }
-
- return $success;
- };
-
- /************/
- /* Factions */
- /************/
- $scripts[] = function() use ($exAdd)
- {
- $success = true;
- $condition = array( // todo (med): exclude non-gaining reputation-header
- CFG_SQL_LIMIT_NONE,
- [['cuFlags', CUSTOM_EXCLUDE_FOR_LISTVIEW, '&'], 0]
- );
- $factionz = new FactionList($condition);
-
- foreach (CLISetup::$localeIds as $l)
- {
- set_time_limit(5);
-
- User::useLocale($l);
- Lang::load(Util::$localeStrings[$l]);
-
- $buff = "var _ = g_factions;\n";
- foreach ($factionz->getListviewData() as $id => $data)
- $buff .= '_['.$id.'] = '.Util::toJSON($data).";\n";
-
- $buff .= "\ng_faction_order = [0, 469, 891, 1037, 1118, 67, 1052, 892, 936, 1117, 169, 980, 1097];\n";
-
- if (!CLISetup::writeFile('datasets/'.User::$localeString.'/p-factions', $buff))
- $success = false;
- }
-
- return $success;
- };
-
- /***********/
- /* Recipes */
- /***********/
- $scripts[] = function() use ($exAdd)
- {
- // special case: secondary skills are always requested, so put them in one single file (185, 129, 356); it also contains g_skill_order
- $skills = [171, 164, 333, 202, 182, 773, 755, 165, 186, 393, 197, [185, 129, 356]];
- $success = true;
- $baseCnd = array(
- CFG_SQL_LIMIT_NONE,
- [['cuFlags', CUSTOM_EXCLUDE_FOR_LISTVIEW, '&'], 0],
- ['effect1Id', [6, 45, 57, 127, 33, 158, 99, 28, 95], '!'], // aura, tradeSkill, Tracking, Prospecting, Decipher, Milling, Disenchant, Summon (Engineering), Skinning
- ['effect2Id', [118, 60], '!'], // not the skill itself
- ['OR', ['typeCat', 9], ['typeCat', 11]]
- );
-
- foreach ($skills as $s)
- {
- $file = is_array($s) ? 'sec' : (string)$s;
- $cnd = array_merge($baseCnd, [['skillLine1', $s]]);
- $recipez = new SpellList($cnd);
- $created = '';
- foreach ($recipez->iterate() as $__)
- {
- foreach ($recipez->canCreateItem() as $idx)
- {
- $id = $recipez->getField('effect'.$idx.'CreateItemId');
- $created .= "g_items.add(".$id.", {'icon':'".$recipez->relItems->getEntry($id)['iconString']."'});\n";
- }
- }
-
- foreach (CLISetup::$localeIds as $l)
- {
- set_time_limit(10);
-
- User::useLocale($l);
- Lang::load(Util::$localeStrings[$l]);
-
- $buff = '';
- foreach ($recipez->getListviewData() as $id => $data)
- $buff .= '_['.$id.'] = '.Util::toJSON($data).";\n";
-
- if (!$buff)
- {
- // this behaviour is intended, do not create an error
- CLI::write('profiler - file datasets/'.User::$localeString.'/p-recipes-'.$file.' has no content => skipping', CLI::LOG_INFO);
- continue;
- }
-
- $buff = $created."\nvar _ = g_spells;\n".$buff;
-
- if (is_array($s))
- $buff .= "\ng_skill_order = [171, 164, 333, 202, 182, 773, 755, 165, 186, 393, 197, 185, 129, 356];\n";
-
- if (!CLISetup::writeFile('datasets/'.User::$localeString.'/p-recipes-'.$file, $buff))
- $success = false;
- }
- }
-
- return $success;
- };
-
- /****************/
- /* Achievements */
- /****************/
- $scripts[] = function() use ($exAdd)
- {
- $success = true;
- $condition = array(
- CFG_SQL_LIMIT_NONE,
- [['cuFlags', CUSTOM_EXCLUDE_FOR_LISTVIEW, '&'], 0],
- [['flags', 1, '&'], 0], // no statistics
- );
- $achievez = new AchievementList($condition);
-
- foreach (CLISetup::$localeIds as $l)
- {
- set_time_limit(5);
-
- User::useLocale($l);
- Lang::load(Util::$localeStrings[$l]);
-
- $sumPoints = 0;
- $buff = "var _ = g_achievements;\n";
- foreach ($achievez->getListviewData(ACHIEVEMENTINFO_PROFILE) as $id => $data)
- {
- $sumPoints += $data['points'];
- $buff .= '_['.$id.'] = '.Util::toJSON($data).";\n";
- }
-
- // categories to sort by
- $buff .= "\ng_achievement_catorder = [92, 14863, 97, 169, 170, 171, 172, 14802, 14804, 14803, 14801, 95, 161, 156, 165, 14806, 14921, 96, 201, 160, 14923, 14808, 14805, 14778, 14865, 14777, 14779, 155, 14862, 14861, 14864, 14866, 158, 162, 14780, 168, 14881, 187, 14901, 163, 14922, 159, 14941, 14961, 14962, 14981, 15003, 15002, 15001, 15041, 15042, 81]";
- // sum points
- $buff .= "\ng_achievement_points = [".$sumPoints."];\n";
-
- if (!CLISetup::writeFile('datasets/'.User::$localeString.'/achievements', $buff))
- $success = false;
- }
-
- return $success;
- };
-
- /******************/
- /* Quick Excludes */
- /******************/
- $scripts[] = function() use (&$exclusions)
- {
- set_time_limit(2);
-
- CLI::write('applying '.count($exclusions).' baseline exclusions');
- DB::Aowow()->query('DELETE FROM ?_profiler_excludes WHERE comment = ""');
-
- foreach ($exclusions as $ex)
- DB::Aowow()->query('REPLACE INTO ?_profiler_excludes (?#) VALUES (?a)', array_keys($ex), array_values($ex));
-
- // excludes; type => [excludeGroupBit => [typeIds]]
- $excludes = [];
-
- $exData = DB::Aowow()->selectCol('SELECT `type` AS ARRAY_KEY, `typeId` AS ARRAY_KEY2, `groups` FROM ?_profiler_excludes');
- for ($i = 0; (1 << $i) < PR_EXCLUDE_GROUP_ANY; $i++)
- foreach ($exData as $type => $data)
- if ($ids = array_keys(array_filter($data, function ($x) use ($i) { return $x & (1 << $i); } )))
- $excludes[$type][$i + 1] = $ids;
-
- $buff = "g_excludes = ".Util::toJSON($excludes ?: (new Stdclass)).";\n";
-
- return CLISetup::writeFile('datasets/quick-excludes', $buff);
- };
-
- // check directory-structure
- foreach (Util::$localeStrings as $dir)
- if (!CLISetup::writeDir('datasets/'.$dir))
- $success = false;
-
- // run scripts
- foreach ($scripts as $func)
- if (!$func())
- $success = false;
-
- return $success;
- }
-
-?>
diff --git a/setup/tools/filegen/profiler.ss.php b/setup/tools/filegen/profiler.ss.php
new file mode 100644
index 00000000..835e229e
--- /dev/null
+++ b/setup/tools/filegen/profiler.ss.php
@@ -0,0 +1,433 @@
+ [[ ], CLISetup::ARGV_PARAM, 'Generates data dumps and completion exclusion filters for the profiler tool.'],
+ 'quests' => [['1'], CLISetup::ARGV_OPTIONAL, '...available quests by category'],
+ 'titles' => [['2'], CLISetup::ARGV_OPTIONAL, '...available titles by gender'],
+ 'mounts' => [['3'], CLISetup::ARGV_OPTIONAL, '...available mounts'],
+ 'companions' => [['4'], CLISetup::ARGV_OPTIONAL, '...available companions'],
+ 'factions' => [['5'], CLISetup::ARGV_OPTIONAL, '...available factions'],
+ 'recipes' => [['6'], CLISetup::ARGV_OPTIONAL, '...available recipes by skill'],
+ 'achievements' => [['7'], CLISetup::ARGV_OPTIONAL, '...available achievements'],
+ 'quickexcludes' => [['9'], CLISetup::ARGV_OPTIONAL, '...unobtainable items, mutually exclusive recipes, factions, etc.'],
+ );
+
+ protected $localized = true;
+ protected $requiredDirs = ['datasets/'];
+ protected $worldDependency = ['player_factionchange_spells', 'conditions'];
+ protected $setupAfter = [['quests', 'quests_startend', 'items', 'currencies', 'titles', 'spell', 'factions', 'achievement'], []];
+
+ private $spellFactions = [];
+ private $exclusions = [];
+ private $opts = [];
+
+ public function generate() : bool
+ {
+ $anyOpt = array_keys($this->info);
+ $this->opts = CLISetup::getOpt(...$anyOpt);
+
+ $this->opts = array_filter($this->opts);
+ if (!$this->opts) // none were set -> use default (all)
+ $this->opts = array_fill_keys(array_slice($anyOpt, 1), true);
+
+
+ $this->spellFactions = DB::World()->selectCol('SELECT `alliance_id` AS ARRAY_KEY, 1 FROM player_factionchange_spells UNION SELECT `horde_id` AS ARRAY_KEY, 2 FROM player_factionchange_spells');
+
+
+ foreach ($this->opts as $fn => $_)
+ $this->$fn();
+
+ return $this->success;
+ }
+
+ private function quests() : void
+ {
+ $questorder = [];
+ $questtotal = [];
+ $condition = [
+ DB::AND,
+ [['cuFlags', CUSTOM_EXCLUDE_FOR_LISTVIEW | CUSTOM_UNAVAILABLE | CUSTOM_DISABLED, '&'], 0],
+ [['flags', QUEST_FLAG_DAILY | QUEST_FLAG_WEEKLY | QUEST_FLAG_TRACKING, '&'], 0],
+ [['specialFlags', QUEST_FLAG_SPECIAL_REPEATABLE | QUEST_FLAG_SPECIAL_DUNGEON_FINDER | QUEST_FLAG_SPECIAL_MONTHLY, '&'], 0]
+ ];
+
+ foreach (Game::QUEST_CLASSES as $cat2 => $cat)
+ {
+ if ($cat2 < 0)
+ continue;
+
+ $cond = array_merge($condition, [['questSortId', $cat]]);
+ $questz = new QuestList($cond);
+ if ($questz->error)
+ continue;
+
+ $questorder[] = $cat2;
+ $questtotal[$cat2] = [];
+
+ // get quests for exclusion
+ foreach ($questz->iterate() as $id => $__)
+ {
+ $this->sumTotal($questtotal[$cat2], $questz->getField('reqRaceMask') ?: -1, $questz->getField('reqClassMask') ?: -1);
+ if ($skillEx = $this->getExcludeForSkill($questz->getField('reqSkillId')))
+ $this->addExclusion(Type::QUEST, $id, $skillEx);
+ }
+
+ $_ = [];
+ $currencies = array_column($questz->rewards, Type::CURRENCY);
+ foreach ($currencies as $curr)
+ foreach ($curr as $cId => $qty)
+ $_[] = $cId;
+
+ $relCurr = new CurrencyList(array(['id', $_]));
+
+ foreach (CLISetup::$locales as $loc)
+ {
+ set_time_limit(20);
+
+ Lang::load($loc);
+
+ if (!$relCurr->error)
+ {
+ $buff = "var _ = g_gatheredcurrencies;\n";
+ foreach ($relCurr->getListviewData() as $id => $data)
+ $buff .= '_['.$id.'] = '.Util::toJSON($data).";\n";
+ }
+
+ $buff .= "var _ = g_quests;\n";
+ foreach ($questz->getListviewData() as $id => $data)
+ $buff .= '_['.$id.'] = '.Util::toJSON($data).";\n";
+
+ if (!CLISetup::writeFile('datasets/'.$loc->json().'/p-quests-'.$cat2, $buff))
+ $this->success = false;
+ }
+ }
+
+ $buff = "g_quest_catorder = ".Util::toJSON($questorder).";\n";
+ $buff .= "g_quest_catorder_total = {};\n";
+ foreach ($questtotal as $cat => $totals)
+ $buff .= "g_quest_catorder_total[".$cat."] = ".Util::toJSON($totals).";\n";
+
+ if (!CLISetup::writeFile('datasets/p-quests', $buff))
+ $this->success = false;
+ }
+
+ private function titles(): void
+ {
+ $titlez = new TitleList(array([['cuFlags', CUSTOM_EXCLUDE_FOR_LISTVIEW, '&'], 0]));
+
+ // get titles for exclusion
+ foreach ($titlez->iterate() as $id => $__)
+ if (empty($titlez->sources[$id][SRC_QUEST]) && empty($titlez->sources[$id][SRC_ACHIEVEMENT]))
+ $this->addExclusion(Type::TITLE, $id, PR_EXCLUDE_GROUP_UNAVAILABLE);
+
+ foreach (CLISetup::$locales as $loc)
+ {
+ set_time_limit(5);
+
+ Lang::load($loc);
+
+ foreach ([GENDER_MALE, GENDER_FEMALE] as $g)
+ {
+ $buff = "var _ = g_titles;\n";
+ foreach ($titlez->getListviewData() as $id => $data)
+ {
+ $data['name'] = Util::localizedString($titlez->getEntry($id), $g ? 'female' : 'male');
+ unset($data['namefemale']);
+ $buff .= '_['.$id.'] = '.Util::toJSON($data).";\n";
+ }
+
+ if (!CLISetup::writeFile('datasets/'.$loc->json().'/p-titles-'.$g, $buff))
+ $this->success = false;
+ }
+ }
+ }
+
+ private function mounts() : void
+ {
+ $condition = array(
+ [['cuFlags', CUSTOM_EXCLUDE_FOR_LISTVIEW, '&'], 0],
+ ['typeCat', -5],
+ ['castTime', 0, '!']
+ );
+ $mountz = new SpellList($condition);
+
+ $conditionSet = DB::World()->selectCol('SELECT `SourceEntry` AS ARRAY_KEY, `ConditionValue1` FROM conditions WHERE `SourceTypeOrReferenceId` = %i AND `ConditionTypeOrReference` = %i AND `SourceEntry` IN %in', Conditions::SRC_SPELL, Conditions::SKILL, $mountz->getFoundIDs());
+
+ // get mounts for exclusion
+ foreach ($conditionSet as $mount => $skill)
+ if ($skillEx = $this->getExcludeForSkill($skill))
+ $this->addExclusion(Type::SPELL, $mount, $skillEx);
+
+ foreach ($mountz->iterate() as $id => $_)
+ if (!$mountz->getSources())
+ $this->addExclusion(Type::SPELL, $id, PR_EXCLUDE_GROUP_UNAVAILABLE);
+
+ foreach (CLISetup::$locales as $loc)
+ {
+ set_time_limit(5);
+
+ Lang::load($loc);
+
+ $buff = "var _ = g_spells;\n";
+ foreach ($mountz->getListviewData(ITEMINFO_MODEL) as $id => $data)
+ {
+ // two cases where the spell is unrestricted but the castitem has class restriction (too lazy to formulate ruleset)
+ if ($id == 66906) // Argent Charger
+ $data['reqclass'] = ChrClass::PALADIN->toMask();
+ else if ($id == 54729) // Winged Steed of the Ebon Blade
+ $data['reqclass'] = ChrClass::DEATHKNIGHT->toMask();
+
+ rsort($data['skill']); // riding (777) expected at pos 0
+
+ $data['side'] = $this->spellFactions[$id] ?? SIDE_BOTH;
+ $data['quality'] = $data['name'][0];
+ $data['name'] = mb_substr($data['name'], 1);
+ $buff .= '_['.$id.'] = '.Util::toJSON($data).";\n";
+ }
+
+ if (!CLISetup::writeFile('datasets/'.$loc->json().'/p-mounts', $buff))
+ $this->success = false;
+ }
+ }
+
+ private function companions() : void
+ {
+ $condition = array(
+ [['cuFlags', CUSTOM_EXCLUDE_FOR_LISTVIEW, '&'], 0],
+ ['typeCat', -6]
+ );
+ $companionz = new SpellList($condition);
+ $legit = DB::Aowow()->selectCol('SELECT `spellId2` FROM ::items WHERE `class` = %i AND `subClass` = %i AND `spellId1` IN %in AND `spellId2` IN %in', ITEM_CLASS_MISC, 2, LEARN_SPELLS, $companionz->getFoundIDs());
+
+ foreach ($companionz->iterate() as $id => $_)
+ if (!$companionz->getSources())
+ $this->addExclusion(Type::SPELL, $id, PR_EXCLUDE_GROUP_UNAVAILABLE);
+
+ foreach (CLISetup::$locales as $loc)
+ {
+ set_time_limit(5);
+
+ Lang::load($loc);
+
+ $buff = "var _ = g_spells;\n";
+ foreach ($companionz->getListviewData(ITEMINFO_MODEL) as $id => $data)
+ {
+ if (!in_array($id, $legit))
+ continue;
+
+ $data['side'] = $this->spellFactions[$id] ?? SIDE_BOTH;
+ $data['quality'] = $data['name'][0];
+ $data['name'] = mb_substr($data['name'], 1);
+ $buff .= '_['.$id.'] = '.Util::toJSON($data).";\n";
+ }
+
+ if (!CLISetup::writeFile('datasets/'.$loc->json().'/p-companions', $buff))
+ $this->success = false;
+ }
+ }
+
+ private function factions() : void
+ {
+ $factionz = new FactionList(array([['cuFlags', CUSTOM_EXCLUDE_FOR_LISTVIEW, '&'], 0]));
+
+ foreach (CLISetup::$locales as $loc)
+ {
+ set_time_limit(5);
+
+ Lang::load($loc);
+
+ $buff = "var _ = g_factions;\n";
+ foreach ($factionz->getListviewData() as $id => $data)
+ $buff .= '_['.$id.'] = '.Util::toJSON($data).";\n";
+
+ $buff .= "\ng_faction_order = [0, 469, 891, 1037, 1118, 67, 1052, 892, 936, 1117, 169, 980, 1097];\n";
+
+ if (!CLISetup::writeFile('datasets/'.$loc->json().'/p-factions', $buff))
+ $this->success = false;
+ }
+ }
+
+ private function recipes() : void
+ {
+ // special case: secondary skills are always requested, so put them in one single file (185, 129, 356); it also contains g_skill_order
+ $skills = array(
+ SKILL_ALCHEMY, SKILL_BLACKSMITHING, SKILL_ENCHANTING, SKILL_ENGINEERING, SKILL_HERBALISM,
+ SKILL_INSCRIPTION, SKILL_JEWELCRAFTING, SKILL_LEATHERWORKING, SKILL_MINING, SKILL_SKINNING,
+ SKILL_TAILORING, [SKILL_COOKING, SKILL_FIRST_AID, SKILL_FISHING]
+ );
+
+ $baseCnd = array(
+ [['cuFlags', CUSTOM_EXCLUDE_FOR_LISTVIEW, '&'], 0],
+ // Inscryption Engineering
+ ['effect1Id', [SPELL_EFFECT_APPLY_AURA, SPELL_EFFECT_TRADE_SKILL, SPELL_EFFECT_PROSPECTING, SPELL_EFFECT_OPEN_LOCK, SPELL_EFFECT_MILLING, SPELL_EFFECT_DISENCHANT, SPELL_EFFECT_SUMMON, SPELL_EFFECT_SKINNING], '!'],
+ // not the skill itself
+ ['effect2Id', [SPELL_EFFECT_SKILL, SPELL_EFFECT_PROFICIENCY], '!'],
+ [DB::OR, ['typeCat', 9], ['typeCat', 11]]
+ );
+
+ foreach ($skills as $s)
+ {
+ $file = is_array($s) ? 'sec' : (string)$s;
+ $cnd = array_merge($baseCnd, [['skillLine1', $s]]);
+ $recipez = new SpellList($cnd);
+ $created = '';
+ foreach ($recipez->iterate() as $id => $_)
+ {
+ if (!$recipez->getSources())
+ $this->addExclusion(Type::SPELL, $id, PR_EXCLUDE_GROUP_UNAVAILABLE);
+
+ foreach ($recipez->canCreateItem() as $idx)
+ {
+ $id = $recipez->getField('effect'.$idx.'CreateItemId');
+ $created .= "g_items.add(".$id.", {'icon':'".$recipez->relItems->getEntry($id)['iconString']."'});\n";
+ }
+ }
+
+ foreach (CLISetup::$locales as $loc)
+ {
+ set_time_limit(10);
+
+ Lang::load($loc);
+
+ $buff = '';
+ foreach ($recipez->getListviewData() as $id => $data)
+ {
+ $data['side'] = $this->spellFactions[$id] ?? SIDE_BOTH;
+ $buff .= '_['.$id.'] = '.Util::toJSON($data).";\n";
+ }
+
+ if (!$buff)
+ {
+ // this behaviour is intended, do not create an error
+ CLI::write('[profiler] - file datasets/'.$loc->json().'/p-recipes-'.$file.' has no content => skipping', CLI::LOG_INFO);
+ continue;
+ }
+
+ $buff = $created."\nvar _ = g_spells;\n".$buff;
+
+ if (is_array($s))
+ $buff .= "\ng_skill_order = [171, 164, 333, 202, 182, 773, 755, 165, 186, 393, 197, 185, 129, 356];\n";
+
+ if (!CLISetup::writeFile('datasets/'.$loc->json().'/p-recipes-'.$file, $buff))
+ $this->success = false;
+ }
+ }
+ }
+
+ private function achievements() : void
+ {
+ $condition = array(
+ [['cuFlags', CUSTOM_EXCLUDE_FOR_LISTVIEW, '&'], 0],
+ [['flags', 1, '&'], 0], // no statistics
+ );
+ $achievez = new AchievementList($condition);
+
+ foreach (CLISetup::$locales as $loc)
+ {
+ set_time_limit(5);
+
+ Lang::load($loc);
+
+ $sumPoints = 0;
+ $buff = "var _ = g_achievements;\n";
+ foreach ($achievez->getListviewData(ACHIEVEMENTINFO_PROFILE) as $id => $data)
+ {
+ if ($data['side'] & SIDE_ALLIANCE) // both sides have the same point total
+ $sumPoints += $data['points'];
+
+ $buff .= '_['.$id.'] = '.Util::toJSON($data).";\n";
+ }
+
+ // categories to sort by
+ $buff .= "\ng_achievement_catorder = [92, 14863, 97, 169, 170, 171, 172, 14802, 14804, 14803, 14801, 95, 161, 156, 165, 14806, 14921, 96, 201, 160, 14923, 14808, 14805, 14778, 14865, 14777, 14779, 155, 14862, 14861, 14864, 14866, 158, 162, 14780, 168, 14881, 187, 14901, 163, 14922, 159, 14941, 14961, 14962, 14981, 15003, 15002, 15001, 15041, 15042, 81]";
+ // sum points
+ $buff .= "\ng_achievement_points = [".$sumPoints."];\n";
+
+ if (!CLISetup::writeFile('datasets/'.$loc->json().'/achievements', $buff))
+ $this->success = false;
+ }
+ }
+
+ private function quickexcludes() : void
+ {
+ set_time_limit(2);
+
+ CLI::write('[profiler] applying '.count($this->exclusions).' baseline exclusions');
+ DB::Aowow()->qry('DELETE FROM ::profiler_excludes WHERE `comment` = ""');
+
+ foreach ($this->exclusions as $ex)
+ DB::Aowow()->qry('REPLACE INTO ::profiler_excludes %v', $ex);
+
+ // excludes; type => [excludeGroupBit => [typeIds]]
+ $excludes = [];
+
+ $exData = DB::Aowow()->selectCol('SELECT `type` AS ARRAY_KEY, `typeId` AS ARRAY_KEY2, `groups` FROM ::profiler_excludes');
+ for ($i = 0; (1 << $i) < PR_EXCLUDE_GROUP_ANY; $i++)
+ foreach ($exData as $type => $data)
+ if ($ids = array_keys(array_filter($data, fn($x) => $x & (1 << $i))))
+ $excludes[$type][$i + 1] = $ids;
+
+ $buff = "g_excludes = ".Util::toJSON($excludes ?: (new \StdClass)).";\n";
+
+ if (!CLISetup::writeFile('datasets/quick-excludes', $buff))
+ $this->success = false;
+ }
+
+ private function getExcludeForSkill(int $skillId) : int
+ {
+ return match ($skillId)
+ {
+ SKILL_FISHING => PR_EXCLUDE_GROUP_REQ_FISHING,
+ SKILL_ENGINEERING => PR_EXCLUDE_GROUP_REQ_ENGINEERING,
+ SKILL_TAILORING => PR_EXCLUDE_GROUP_REQ_TAILORING,
+ default => 0
+ };
+ }
+
+ private function addExclusion(int $type, int $typeId, int $groups, string $comment = '') : void
+ {
+ $k = $type.'-'.$typeId;
+
+ if (!isset($this->exclusions[$k]))
+ $this->exclusions[$k] = ['type' => $type, 'typeId' => $typeId, 'groups' => $groups, 'comment' => $comment];
+ else
+ {
+ $this->exclusions[$k]['groups'] |= $groups;
+ if ($comment)
+ $this->exclusions[$k]['comment'] .= '; '.$comment;
+ }
+ }
+
+ private function sumTotal(array &$sumArr, int $raceMask = -1, int $classMask= -1) : void
+ {
+ foreach (ChrRace::cases() as $ra)
+ {
+ if (!$ra->matches($raceMask))
+ continue;
+
+ foreach (ChrClass::cases() as $cl)
+ {
+ if (!$cl->matches($classMask))
+ continue;
+
+ if (!isset($sumArr[$ra->value][$cl->value]))
+ $sumArr[$ra->value][$cl->value] = 1;
+ else
+ $sumArr[$ra->value][$cl->value]++;
+ }
+ }
+ }
+});
+
+?>
diff --git a/setup/tools/filegen/realmMenu.func.php b/setup/tools/filegen/realmMenu.func.php
deleted file mode 100644
index a25bb5ac..00000000
--- a/setup/tools/filegen/realmMenu.func.php
+++ /dev/null
@@ -1,71 +0,0 @@
- $n)
- $subs[$idx] = [];
-
- foreach (Profiler::getRealms() as $row)
- {
- $idx = array_search($row['region'], Util::$regions);
- if ($idx !== false)
- {
- $set |= (1 << $idx);
- $subs[$idx][] = [Profiler::urlize($row['name'], true), $row['name']];
- }
-
- }
- if (!$set)
- CLI::write(' - realmMenu: Auth-DB not set up .. realm menu will be empty', CLI::LOG_WARN);
-
- foreach (Util::$regions as $idx => $n)
- if ($set & (1 << $idx))
- $menu[] = [$n, Lang::profiler('regions', $n), null, &$subs[$idx]];
-
- return Util::toJSON($menu);
- }
-?>
diff --git a/setup/tools/filegen/realmmenu.ss.php b/setup/tools/filegen/realmmenu.ss.php
new file mode 100644
index 00000000..ca4ae692
--- /dev/null
+++ b/setup/tools/filegen/realmmenu.ss.php
@@ -0,0 +1,89 @@
+ [[], CLISetup::ARGV_PARAM, 'Generates \'profile_all.js\'-file that extends the profiler menus.']
+ );
+
+ protected $fileTemplateSrc = ['profile_all.js.in'];
+ protected $fileTemplateDest = ['static/js/profile_all.js'];
+ protected $worldDependency = ['realmlist'];
+
+ private function realmMenu() : string
+ {
+ $subs = [];
+ $set = 0x0;
+ $menu = [
+ // skip usage of battlegroup
+ // ['us', Lang::profiler('regions', 'us'), null,[[Profiler::urlize(Cfg::get('BATTLEGROUP')), Cfg::get('BATTLEGROUP'), null, &$subUS]]],
+ // ['eu', Lang::profiler('regions', 'eu'), null,[[Profiler::urlize(Cfg::get('BATTLEGROUP')), Cfg::get('BATTLEGROUP'), null, &$subEU]]]
+ ];
+
+ foreach (Util::$regions as $idx => $n)
+ $subs[$idx] = [];
+
+ if (!DB::isConnectable(DB_AUTH))
+ CLI::write('[realmmenu] Auth DB not set up .. realm menu will be empty', CLI::LOG_WARN);
+ else
+ foreach (Profiler::getRealms() as $row)
+ {
+ $idx = array_search($row['region'], Util::$regions);
+ if ($idx === false)
+ continue;
+
+ $set |= (1 << $idx);
+ $subs[$idx][] = [Profiler::urlize($row['name'], true), $row['name'], null, null, $row['access'] ? ['requiredAccess' => $row['access']] : null];
+ }
+
+ if (!$set)
+ CLI::write('[realmmenu] no viable realms found .. realm menu will be empty', CLI::LOG_WARN);
+
+ // why is this file not localized!?
+ Lang::load(Locale::EN);
+
+ foreach (Util::$regions as $idx => $n)
+ if ($set & (1 << $idx))
+ $menu[] = [$n, Lang::profiler('regions', $n), null, &$subs[$idx]];
+
+ return Util::toJSON($menu);
+ }
+});
+
+?>
diff --git a/setup/tools/filegen/realms.func.php b/setup/tools/filegen/realms.func.php
deleted file mode 100644
index 44453f19..00000000
--- a/setup/tools/filegen/realms.func.php
+++ /dev/null
@@ -1,44 +0,0 @@
- 'www.wowarmory.com',
- eu => 'eu.wowarmory.com'
- };
- */
-
- /* Examples
- 1 => {
- name:'Eldre\'Thalas',
- battlegroup:'Reckoning',
- region:'us'
- },
- */
-
- function realms()
- {
- $realms = Profiler::getRealms();
- if (!$realms)
- CLI::write(' - realms: Auth-DB not set up .. static data g_realms will be empty', CLI::LOG_WARN);
- // else
- // foreach ($realms as &$r)
- // $r['battlegroup'] = CFG_BATTLEGROUP;
-
- $toFile = "var g_realms = ".Util::toJSON($realms).";";
- $file = 'datasets/realms';
-
- return CLISetup::writeFile($file, $toFile);
- }
-
-?>
diff --git a/setup/tools/filegen/realms.ss.php b/setup/tools/filegen/realms.ss.php
new file mode 100644
index 00000000..f8ab9bde
--- /dev/null
+++ b/setup/tools/filegen/realms.ss.php
@@ -0,0 +1,58 @@
+ 'www.wowarmory.com',
+ eu => 'eu.wowarmory.com'
+ };
+*/
+
+/* Examples
+ 1 => {
+ name:'Eldre\'Thalas',
+ battlegroup:'Reckoning',
+ region:'us'
+ },
+*/
+
+CLISetup::registerSetup("build", new class extends SetupScript
+{
+ protected $info = array(
+ 'realms' => [[], CLISetup::ARGV_PARAM, 'Generates \'realms\'-file to be referenced by the profiler tool.']
+ );
+
+ protected $worldDependency = ['realmlist'];
+ protected $requiredDirs = ['datasets/'];
+
+ public function generate() : bool
+ {
+ $realms = [];
+
+ if (!DB::isConnectable(DB_AUTH))
+ CLI::write('[realms] Auth DB not set up .. static data g_realms will be empty', CLI::LOG_WARN);
+ else if (!($realms = Profiler::getRealms()))
+ CLI::write('[realms] no viable realms found .. static data g_realms will be empty', CLI::LOG_WARN);
+ // else
+ // foreach ($realms as &$r)
+ // $r['battlegroup'] = Cfg::get('BATTLEGROUP');
+
+ // remove access column
+ array_walk($realms, function (&$x) { unset($x['access']); });
+
+ $toFile = "var g_realms = ".Util::toJSON($realms).";";
+ $file = 'datasets/realms';
+
+ return CLISetup::writeFile($file, $toFile);
+ }
+});
+
+?>
diff --git a/setup/tools/filegen/robots.ss.php b/setup/tools/filegen/robots.ss.php
new file mode 100644
index 00000000..b9f8447c
--- /dev/null
+++ b/setup/tools/filegen/robots.ss.php
@@ -0,0 +1,24 @@
+ [[], CLISetup::ARGV_PARAM, 'Fills robots.txt with site variables.']
+ );
+
+ protected $fileTemplateSrc = ['robots.txt.in'];
+ protected $fileTemplateDest = ['robots.txt']; // aowow root
+});
+
+?>
diff --git a/setup/tools/filegen/searchbox.ss.php b/setup/tools/filegen/searchbox.ss.php
new file mode 100644
index 00000000..91d7c2ee
--- /dev/null
+++ b/setup/tools/filegen/searchbox.ss.php
@@ -0,0 +1,24 @@
+ [[], CLISetup::ARGV_PARAM, 'Fills search widget files (static/widgets/searchbox*) with site variables.']
+ );
+
+ protected $fileTemplateSrc = ['searchbox.js.in', 'searchbox.html.in'];
+ protected $fileTemplateDest = ['static/widgets/searchbox.js', 'static/widgets/searchbox/searchbox.html'];
+});
+
+?>
diff --git a/setup/tools/filegen/searchplugin.ss.php b/setup/tools/filegen/searchplugin.ss.php
new file mode 100644
index 00000000..170c7557
--- /dev/null
+++ b/setup/tools/filegen/searchplugin.ss.php
@@ -0,0 +1,25 @@
+ [[], CLISetup::ARGV_PARAM, 'Fills browser opensearch plugin (static/download/searchplugins/aowow.xml) with site variables.']
+ );
+
+ protected $fileTemplateSrc = ['aowow.xml.in'];
+ protected $fileTemplateDest = ['static/download/searchplugins/aowow.xml'];
+ protected $requiredDirs = ['static/download/searchplugins/'];
+});
+
+?>
diff --git a/setup/tools/filegen/simpleImg.func.php b/setup/tools/filegen/simpleImg.func.php
deleted file mode 100644
index 595ac452..00000000
--- a/setup/tools/filegen/simpleImg.func.php
+++ /dev/null
@@ -1,473 +0,0 @@
- ['Icons/', $iconDirs, '/.*\.blp$', true, 0, null],
- 1 => ['Spellbook/', [['Interface/Spellbook/', '.png', 0, 0, 0]], '/UI-Glyph-Rune-?\d+.blp$', true, 0, null],
- 2 => ['PaperDoll/', array_slice($iconDirs, 0, 3), '/UI-(Backpack|PaperDoll)-.*\.blp$', true, 0, null],
- 3 => ['GLUES/CHARACTERCREATE/UI-CharacterCreate-Races.blp', $iconDirs, '', true, 64, null],
- 4 => ['GLUES/CHARACTERCREATE/UI-CharacterCreate-CLASSES.blp', $iconDirs, '', true, 64, null],
- 5 => ['GLUES/CHARACTERCREATE/UI-CharacterCreate-Factions.blp', $iconDirs, '', true, 64, null],
- // 6 => ['Minimap/OBJECTICONS.BLP', [['icons/tiny/', '.gif', 0, 16, 2]], '', true, 32, null],
- 7 => ['FlavorImages/', [['Interface/FlavorImages/', '.png', 0, 0, 0]], '/.*\.blp$', false, 0, null],
- 8 => ['Pictures/', [['Interface/Pictures/', '.png', 0, 0, 0]], '/.*\.blp$', false, 0, null],
- 9 => ['PvPRankBadges/', [['Interface/PvPRankBadges/', '.png', 0, 0, 0]], '/.*\.blp$', false, 0, null],
- 10 => ['Calendar/Holidays/', $calendarDirs, '/.*(start|[ayhs])\.blp$', true, 0, null],
- 11 => ['GLUES/LOADINGSCREENS/', $loadScreenDirs, '/lo.*\.blp$', false, 0, null]
- );
- // textures are composed of 64x64 icons
- // numeric indexed arrays mimick the position on the texture
- $cuNames = array(
- 2 => array(
- 'ui-paperdoll-slot-chest' => 'inventoryslot_chest',
- 'ui-backpack-emptyslot' => 'inventoryslot_empty',
- 'ui-paperdoll-slot-feet' => 'inventoryslot_feet',
- 'ui-paperdoll-slot-finger' => 'inventoryslot_finger',
- 'ui-paperdoll-slot-hands' => 'inventoryslot_hands',
- 'ui-paperdoll-slot-head' => 'inventoryslot_head',
- 'ui-paperdoll-slot-legs' => 'inventoryslot_legs',
- 'ui-paperdoll-slot-mainhand' => 'inventoryslot_mainhand',
- 'ui-paperdoll-slot-neck' => 'inventoryslot_neck',
- 'ui-paperdoll-slot-secondaryhand' => 'inventoryslot_offhand',
- 'ui-paperdoll-slot-ranged' => 'inventoryslot_ranged',
- 'ui-paperdoll-slot-relic' => 'inventoryslot_relic',
- 'ui-paperdoll-slot-shirt' => 'inventoryslot_shirt',
- 'ui-paperdoll-slot-shoulder' => 'inventoryslot_shoulder',
- 'ui-paperdoll-slot-tabard' => 'inventoryslot_tabard',
- 'ui-paperdoll-slot-trinket' => 'inventoryslot_trinket',
- 'ui-paperdoll-slot-waist' => 'inventoryslot_waist',
- 'ui-paperdoll-slot-wrists' => 'inventoryslot_wrists'
- ),
- 3 => array( // uses nameINT from ChrRaces.dbc
- ['race_human_male', 'race_dwarf_male', 'race_gnome_male', 'race_nightelf_male', 'race_draenei_male' ],
- ['race_tauren_male', 'race_scourge_male', 'race_troll_male', 'race_orc_male', 'race_bloodelf_male' ],
- ['race_human_female', 'race_dwarf_female', 'race_gnome_female', 'race_nightelf_female', 'race_draenei_female' ],
- ['race_tauren_female', 'race_scourge_female', 'race_troll_female', 'race_orc_female', 'race_bloodelf_female']
- ),
- 4 => array( // uses nameINT from ChrClasses.dbc
- ['class_warrior', 'class_mage', 'class_rogue', 'class_druid' ],
- ['class_hunter', 'class_shaman', 'class_priest', 'class_warlock'],
- ['class_paladin', 'class_deathknight' ]
- ),
- 5 => array(
- ['faction_alliance', 'faction_horde']
- ),
- 6 => array(
- [],
- [null, 'quest_start', 'quest_end', 'quest_start_daily', 'quest_end_daily']
- ),
- 10 => array( // really should have read holidays.dbc...
- 'calendar_winterveilstart' => 'calendar_winterveilstart',
- 'calendar_noblegardenstart' => 'calendar_noblegardenstart',
- 'calendar_childrensweekstart' => 'calendar_childrensweekstart',
- 'calendar_fishingextravaganza' => 'calendar_fishingextravaganzastart',
- 'calendar_harvestfestivalstart' => 'calendar_harvestfestivalstart',
- 'calendar_hallowsendstart' => 'calendar_hallowsendstart',
- 'calendar_lunarfestivalstart' => 'calendar_lunarfestivalstart',
- 'calendar_loveintheairstart' => 'calendar_loveintheairstart',
- 'calendar_midsummerstart' => 'calendar_midsummerstart',
- 'calendar_brewfeststart' => 'calendar_brewfeststart',
- 'calendar_darkmoonfaireelwynnstart' => 'calendar_darkmoonfaireelwynnstart',
- 'calendar_darkmoonfairemulgorestart' => 'calendar_darkmoonfairemulgorestart',
- 'calendar_darkmoonfaireterokkarstart' => 'calendar_darkmoonfaireterokkarstart',
- 'calendar_piratesday' => 'calendar_piratesdaystart',
- 'calendar_wotlklaunch' => 'calendar_wotlklaunchstart',
- 'calendar_dayofthedeadstart' => 'calendar_dayofthedeadstart',
- 'calendar_fireworks' => 'calendar_fireworksstart'
- )
- );
-
- $writeImage = function($name, $ext, $src, $srcDims, $destDims, $done)
- {
- $ok = false;
- $dest = imagecreatetruecolor($destDims['w'], $destDims['h']);
-
- imagesavealpha($dest, true);
- if ($ext == '.png')
- imagealphablending($dest, false);
-
- imagecopyresampled($dest, $src, $destDims['x'], $destDims['x'], $srcDims['x'], $srcDims['y'], $destDims['w'], $destDims['h'], $srcDims['w'], $srcDims['h']);
-
- switch ($ext)
- {
- case '.jpg':
- $ok = imagejpeg($dest, $name.$ext, 85);
- break;
- case '.gif':
- $ok = imagegif($dest, $name.$ext);
- break;
- case '.png':
- $ok = imagepng($dest, $name.$ext);
- break;
- default:
- CLI::write($done.' - unsupported file fromat: '.$ext, CLI::LOG_WARN);
- }
-
- imagedestroy($dest);
-
- if ($ok)
- {
- chmod($name.$ext, Util::FILE_ACCESS);
- CLI::write($done.' - image '.$name.$ext.' written', CLI::LOG_OK);
- }
- else
- CLI::write($done.' - could not create image '.$name.$ext, CLI::LOG_ERROR);
-
- return $ok;
- };
-
- $checkSourceDirs = function($sub) use ($imgPath, &$paths)
- {
- $hasMissing = false;
- foreach ($paths as $pathIdx => [$subDir, , , , , $realPath])
- {
- if ($realPath)
- continue;
-
- $p = sprintf($imgPath, $sub).$subDir;
- if (CLISetup::fileExists($p))
- $paths[$pathIdx][5] = $p;
- else
- $hasMissing = true;
- }
-
- return !$hasMissing;
- };
-
- if (CLISetup::getOpt('icons'))
- array_push($groups, 0, 2, 3, 4, 5, 10);
- if (CLISetup::getOpt('glyphs'))
- $groups[] = 1;
- if (CLISetup::getOpt('pagetexts'))
- array_push($groups, 7, 8, 9);
- if (CLISetup::getOpt('loadingscreens'))
- $groups[] = 11;
-
- // filter by passed options
- if (!$groups) // by default do not generate loadingscreens
- unset($paths[11]);
- else
- foreach (array_keys($paths) as $k)
- if (!in_array($k, $groups))
- unset($paths[$k]);
-
- foreach (CLISetup::$expectedPaths as $xp => $locId)
- {
- if (!in_array($locId, CLISetup::$localeIds))
- continue;
-
- if ($xp) // if in subDir add trailing slash
- $xp .= '/';
-
- if ($checkSourceDirs($xp))
- break;
- }
-
- $locList = [];
- foreach (CLISetup::$expectedPaths as $xp => $locId)
- if (in_array($locId, CLISetup::$localeIds))
- $locList[] = $xp;
-
- CLI::write('required resources overview:', CLI::LOG_INFO);
- foreach ($paths as [$path, , , , , $realPath])
- {
- if ($realPath)
- CLI::write(CLI::green(' FOUND ').' - '.str_pad($path, 53).' @ '.$realPath);
- else
- CLI::write(CLI::red('MISSING').' - '.str_pad($path, 53).' @ '.sprintf($imgPath, '['.implode(',', $locList).']/').$path);
- }
-
- CLI::write();
-
- // if no subdir had sufficient data, diaf
- if (count(array_filter(array_column($paths, 5))) != count($paths))
- {
- CLI::write('one or more required directories are missing:', CLI::LOG_ERROR);
- foreach ($missing as $m)
- CLI::write(' - '.$m, CLI::LOG_ERROR);
-
- return;
- }
- else
- sleep(1);
-
- // init directories
- foreach (array_column($paths, 1) as $subDirs)
- foreach ($subDirs as $sd)
- if (!CLISetup::writeDir($destDir.$sd[0]))
- $success = false;
-
- // ok, departure from std::procedure here
- // scan ItemDisplayInfo.dbc and SpellIcon.dbc for expected images and save them to an array
- // load all icon paths into another array and xor these two
- // excess entries for the directory are fine, excess entries for the dbc's are not
- $dbcEntries = [];
-
- if (isset($paths[0]) || isset($paths[1])) // generates icons or glyphs
- {
- if (isset($paths[0]) && !isset($paths[1]))
- $siRows = DB::Aowow()->selectCol('SELECT iconPath FROM dbc_spellicon WHERE iconPath NOT LIKE "%glyph-rune%"');
- else if (!isset($paths[0]) && isset($paths[1]))
- $siRows = DB::Aowow()->selectCol('SELECT iconPath FROM dbc_spellicon WHERE iconPath LIKE "%glyph-rune%"');
- else
- $siRows = DB::Aowow()->selectCol('SELECT iconPath FROM dbc_spellicon');
-
- foreach ($siRows as $icon)
- {
- if (stristr($icon, $paths[0][0])) // Icons/
- $dbcEntries[] = strtolower($paths[0][5].substr(strrchr($icon, '\\'), 1));
- else if (stristr($icon, $paths[1][0])) // Spellbook/
- $dbcEntries[] = strtolower($paths[1][5].substr(strrchr($icon, '\\'), 1));
- }
- }
-
- if (isset($paths[0]))
- {
- $itemIcons = DB::Aowow()->selectCol('SELECT inventoryIcon1 FROM dbc_itemdisplayinfo WHERE inventoryIcon1 <> ""');
- foreach ($itemIcons as $icon)
- $dbcEntries[] = strtolower($paths[0][5].'/'.$icon.'.blp');
-
- $eventIcons = DB::Aowow()->selectCol('SELECT textureString FROM dbc_holidays WHERE textureString <> ""');
- foreach ($eventIcons as $icon)
- $dbcEntries[] = strtolower($paths[10][5].'/'.$icon.'Start.blp');
- }
-
- // case-insensitive array_unique *vomits silently into a corner*
- $dbcEntries = array_intersect_key($dbcEntries, array_unique($dbcEntries));
-
- $allPaths = [];
- foreach ($paths as $i => [$inPath, $outInfo, $pattern, $isIcon, $tileSize, $path])
- {
- $search = $path.$pattern;
- if ($pattern)
- $search = '/'.str_replace('/', '\\/', $search).'/i';
-
- $files = CLISetup::filesInPath($search, !!$pattern);
- $allPaths = array_merge($allPaths, $files);
-
- CLI::write('processing '.count($files).' files in '.$path.'...');
-
- $j = 0;
- foreach ($files as $f)
- {
- ini_set('max_execution_time', 30); // max 30sec per image (loading takes the most time)
-
- $src = null;
- $na = explode('/', $f);
- $img = explode('.', array_pop($na));
- array_pop($img); // there are a hand full of images with multiple file endings or random dots in the name
- $img = implode('.', $img);
-
- // file not from dbc -> name from array or skip file
- if (!empty($cuNames[$i]))
- {
- if (!empty($cuNames[$i][strtolower($img)]))
- $img = $cuNames[$i][strtolower($img)];
- else if (!$tileSize)
- {
- $j += count($outInfo);
- CLI::write('skipping extraneous file '.$img.' (+'.count($outInfo).')');
- continue;
- }
- }
-
- $nFiles = count($outInfo) * ($tileSize ? array_sum(array_map('count', $cuNames[$i])) : count($files));
-
- foreach ($outInfo as [$dest, $ext, $srcSize, $destSize, $borderOffset])
- {
- if ($tileSize)
- {
- foreach ($cuNames[$i] as $y => $row)
- {
- foreach ($row as $x => $name)
- {
- $j++;
- $img = $isIcon ? strtolower($name) : $name;
- $done = ' - '.str_pad($j.'/'.$nFiles, 12).str_pad('('.number_format($j * 100 / $nFiles, 2).'%)', 9);
-
- if (!CLISetup::getOpt('force') && file_exists($destDir.$dest.$img.$ext))
- {
- CLI::write($done.' - file '.$dest.$img.$ext.' was already processed');
- continue;
- }
-
- if (!$src)
- $src = $loadImageFile($f);
-
- if (!$src) // error should be created by imagecreatefromblp
- continue;
-
- /*
- ready for some major bullshitery? well, here it comes anyway!
- the class-icon tile [idx: 4] isn't 64x64 but 63x64 .. the right side border is 1px short
- so if we don't watch out, the icons start to shift over and show the border
- also the icon border is displaced by 1px
- */
- $from = array(
- 'x' => $borderOffset + 1 + ($tileSize - ($i == 4 ? 1 : 0)) * $x,
- 'y' => $borderOffset + 1 + $tileSize * $y,
- 'w' => ($tileSize - ($i == 4 ? 1 : 0)) - $borderOffset * 2,
- 'h' => $tileSize - $borderOffset * 2
- );
- $to = array(
- 'x' => 0,
- 'y' => 0,
- 'w' => $destSize,
- 'h' => $destSize
- );
-
- if (!$writeImage($destDir.$dest.$img, $ext, $src, $from, $to, $done))
- $success = false;
- }
- }
-
- // custom handle for combined icon 'quest_startend'
- /* not used due to alphaChannel issues
- if ($tileSize == 32)
- {
- $dest = imagecreatetruecolor(19, 16);
- imagesavealpha($dest, true);
- imagealphablending($dest, true);
-
- // excalmationmark, questionmark
- imagecopyresampled($dest, $src, 0, 1, 32 + 5, 32 + 2, 8, 15, 18, 30);
- imagecopyresampled($dest, $src, 5, 0, 64 + 1, 32 + 1, 10, 16, 18, 28);
-
- if (imagegif($dest, $destDir.$dest.'quest_startend.gif'))
- CLI::write(' extra - image '.$destDir.$dest.'quest_startend.gif written', CLI::LOG_OK);
- else
- {
- CLI::write(' extra - could not create image '.$destDir.$dest.'quest_startend.gif', CLI::LOG_ERROR);
- $success = false;
- }
-
- imagedestroy($dest);
- }
- */
- }
- else
- {
- // icon -> lowercase
- if ($isIcon)
- $img = strtolower($img);
-
- $j++;
- $done = ' - '.str_pad($j.'/'.$nFiles, 12).str_pad('('.number_format($j * 100 / $nFiles, 2).'%)', 9);
-
- if (!CLISetup::getOpt('force') && file_exists($destDir.$dest.$img.$ext))
- {
- CLI::write($done.' - file '.$dest.$img.$ext.' was already processed');
- continue;
- }
-
- if (!$src)
- $src = $loadImageFile($f);
-
- if (!$src) // error should be created by imagecreatefromblp
- continue;
-
- $from = array(
- 'x' => $borderOffset,
- 'y' => $borderOffset,
- 'w' => ($srcSize ?: imagesx($src)) - $borderOffset * 2,
- 'h' => ($srcSize ?: imagesy($src)) - $borderOffset * 2
- );
- $to = array(
- 'x' => 0,
- 'y' => 0,
- 'w' => $destSize ?: imagesx($src),
- 'h' => $destSize ?: imagesy($src)
- );
-
- if (!$writeImage($destDir.$dest.$img, $ext, $src, $from, $to, $done))
- $success = false;
- }
- }
-
- unset($src);
- }
- }
-
- // reset execTime
- ini_set('max_execution_time', FileGen::$defaultExecTime);
-
- if ($missing = array_diff(array_map('strtolower', $dbcEntries), array_map('strtolower', $allPaths)))
- {
- // hide affected icons from listviews
- $iconNames = array_map(function($path) {
- preg_match('/\/([^\/]+)\.blp$/i', $path, $m);
- return $m ? $m[1] : null;
- }, $missing);
-
- DB::Aowow()->query('UPDATE ?_icons SET cuFlags = cuFlags | ?d WHERE name IN (?a)', CUSTOM_EXCLUDE_FOR_LISTVIEW, $iconNames);
-
- asort($missing);
- CLI::write('the following '.count($missing).' images where referenced by DBC but not in the mpqData directory. They may need to be converted by hand later on.', CLI::LOG_WARN);
- foreach ($missing as $m)
- CLI::write(' - '.$m);
- }
-
- return $success;
- }
diff --git a/setup/tools/filegen/simpleimg.ss.php b/setup/tools/filegen/simpleimg.ss.php
new file mode 100644
index 00000000..6e30aedb
--- /dev/null
+++ b/setup/tools/filegen/simpleimg.ss.php
@@ -0,0 +1,391 @@
+ [[ ], CLISetup::ARGV_PARAM, 'Converts and resizes BLP2 images smaller than 255x255 into required formats (mostly icons)'],
+ 'icons' => [['1'], CLISetup::ARGV_OPTIONAL, 'Generate icons for spells, items, classes, races, ect.'],
+ 'glyphs' => [['2'], CLISetup::ARGV_OPTIONAL, 'Generate decorative glyph symbols displayed on related item and spell pages.'],
+ 'pagetexts' => [['3'], CLISetup::ARGV_OPTIONAL, 'Generate images contained in text on readable items and gameobjects.'],
+ 'loadingscreens' => [['4'], CLISetup::ARGV_OPTIONAL, 'Generate loading screen images (not used on page; skipped by default)']
+ );
+
+ protected $dbcSourceFiles = ['holidays', 'spellicon', 'itemdisplayinfo'];
+ protected $setupAfter = [['icons'], []];
+
+ private const ICON_DIRS = array(
+ ['static/images/wow/icons/large/', 'jpg', 0, ICON_SIZE_LARGE, 4],
+ ['static/images/wow/icons/medium/', 'jpg', 0, ICON_SIZE_MEDIUM, 4],
+ ['static/images/wow/icons/small/', 'jpg', 0, ICON_SIZE_SMALL, 4],
+ ['static/images/wow/icons/tiny/', 'gif', 0, ICON_SIZE_TINY, 4]
+ );
+
+ private $genSteps = array(
+ // srcPath, realPath, localized, [pattern, isIcon, tileSize], [[dest, ext, srcSize, destSize, borderOffset]]
+ 0 => ['Icons/', null, false, ['.*\.(blp|png)$', true, 0], self::ICON_DIRS, ],
+ 1 => ['Spellbook/', null, false, ['UI-Glyph-Rune-?\d+.(blp|png)$', false, 0], [['static/images/wow/Interface/Spellbook/', 'png', 0, 0, 0]]],
+ 2 => ['PaperDoll/', null, false, ['UI-(Backpack|PaperDoll)-.*\.(blp|png)$', true, 0], self::ICON_DIRS, ],
+ 3 => ['GLUES/CHARACTERCREATE/', null, false, ['UI-CharacterCreate-Races\.(blp|png)', true, 64], self::ICON_DIRS, ],
+ 4 => ['GLUES/CHARACTERCREATE/', null, false, ['UI-CharacterCreate-CLASSES\.(blp|png)', true, 64], self::ICON_DIRS, ],
+ 5 => ['GLUES/CHARACTERCREATE/', null, false, ['UI-CharacterCreate-Factions\.(blp|png)', true, 64], self::ICON_DIRS, ],
+ // 6 => ['Minimap/' , null, false, ['OBJECTICONS.(BLP|png)', true, 32], [['static/images/wow/icons/tiny/', 'gif', 0, 16, 2]]],
+ 7 => ['FlavorImages/', null, false, ['.*\.(blp|png)$', false, 0], [['static/images/wow/Interface/FlavorImages/', 'png', 0, 0, 0]]],
+ 8 => ['Pictures/', null, false, ['.*\.(blp|png)$', false, 0], [['static/images/wow/Interface/Pictures/', 'png', 0, 0, 0]]],
+ 9 => ['PvPRankBadges/', null, false, ['.*\.(blp|png)$', false, 0], [['static/images/wow/Interface/PvPRankBadges/', 'png', 0, 0, 0]]],
+ 10 => ['Calendar/Holidays/', null, false, ['.*(start|[ayhs])\.(blp|png)$', true, 0], self::ICON_DIRS, ],
+ 11 => ['GLUES/LOADINGSCREENS/', null, false, ['lo.*\.(blp|png)$', false, 0], [['cache/loadingscreens/', 'png', 0, 0, 0]]],
+ 12 => ['PVPFrame/', null, false, ['PVP-(ArenaPoints|Currency).*\.(blp|png)$', true, 0], self::ICON_DIRS, ]
+ );
+
+ // textures are composed of 64x64 icons
+ // numeric indexed arrays mimick the position on the texture
+ private $cuNames = array(
+ 2 => array(
+ 'ui-paperdoll-slot-chest' => 'inventoryslot_chest',
+ 'ui-backpack-emptyslot' => 'inventoryslot_empty',
+ 'ui-paperdoll-slot-feet' => 'inventoryslot_feet',
+ 'ui-paperdoll-slot-finger' => 'inventoryslot_finger',
+ 'ui-paperdoll-slot-hands' => 'inventoryslot_hands',
+ 'ui-paperdoll-slot-head' => 'inventoryslot_head',
+ 'ui-paperdoll-slot-legs' => 'inventoryslot_legs',
+ 'ui-paperdoll-slot-mainhand' => 'inventoryslot_mainhand',
+ 'ui-paperdoll-slot-neck' => 'inventoryslot_neck',
+ 'ui-paperdoll-slot-secondaryhand' => 'inventoryslot_offhand',
+ 'ui-paperdoll-slot-ranged' => 'inventoryslot_ranged',
+ 'ui-paperdoll-slot-relic' => 'inventoryslot_relic',
+ 'ui-paperdoll-slot-shirt' => 'inventoryslot_shirt',
+ 'ui-paperdoll-slot-shoulder' => 'inventoryslot_shoulder',
+ 'ui-paperdoll-slot-tabard' => 'inventoryslot_tabard',
+ 'ui-paperdoll-slot-trinket' => 'inventoryslot_trinket',
+ 'ui-paperdoll-slot-waist' => 'inventoryslot_waist',
+ 'ui-paperdoll-slot-wrists' => 'inventoryslot_wrists'
+ ),
+ 3 => array( // uses nameINT from ChrRaces.dbc
+ ['race_human_male', 'race_dwarf_male', 'race_gnome_male', 'race_nightelf_male', 'race_draenei_male' ],
+ ['race_tauren_male', 'race_scourge_male', 'race_troll_male', 'race_orc_male', 'race_bloodelf_male' ],
+ ['race_human_female', 'race_dwarf_female', 'race_gnome_female', 'race_nightelf_female', 'race_draenei_female' ],
+ ['race_tauren_female', 'race_scourge_female', 'race_troll_female', 'race_orc_female', 'race_bloodelf_female']
+ ),
+ 4 => array( // uses nameINT from ChrClasses.dbc
+ ['class_warrior', 'class_mage', 'class_rogue', 'class_druid' ],
+ ['class_hunter', 'class_shaman', 'class_priest', 'class_warlock'],
+ ['class_paladin', 'class_deathknight' ]
+ ),
+ 5 => array(
+ ['faction_alliance', 'faction_horde']
+ ),
+ 6 => array(
+ [],
+ [null, 'quest_start', 'quest_end', 'quest_start_daily', 'quest_end_daily']
+ ),
+ 10 => array( // really should have read holidays.dbc...
+ 'calendar_winterveilstart' => 'calendar_winterveilstart',
+ 'calendar_noblegardenstart' => 'calendar_noblegardenstart',
+ 'calendar_childrensweekstart' => 'calendar_childrensweekstart',
+ 'calendar_fishingextravaganza' => 'calendar_fishingextravaganzastart',
+ 'calendar_harvestfestivalstart' => 'calendar_harvestfestivalstart',
+ 'calendar_hallowsendstart' => 'calendar_hallowsendstart',
+ 'calendar_lunarfestivalstart' => 'calendar_lunarfestivalstart',
+ 'calendar_loveintheairstart' => 'calendar_loveintheairstart',
+ 'calendar_midsummerstart' => 'calendar_midsummerstart',
+ 'calendar_brewfeststart' => 'calendar_brewfeststart',
+ 'calendar_darkmoonfaireelwynnstart' => 'calendar_darkmoonfaireelwynnstart',
+ 'calendar_darkmoonfairemulgorestart' => 'calendar_darkmoonfairemulgorestart',
+ 'calendar_darkmoonfaireterokkarstart' => 'calendar_darkmoonfaireterokkarstart',
+ 'calendar_piratesday' => 'calendar_piratesdaystart',
+ 'calendar_wotlklaunch' => 'calendar_wotlklaunchstart',
+ 'calendar_dayofthedeadstart' => 'calendar_dayofthedeadstart',
+ 'calendar_fireworks' => 'calendar_fireworksstart'
+ )
+ );
+
+ public function __construct()
+ {
+ $this->imgPath = CLISetup::$srcDir.$this->imgPath;
+ $this->maxExecTime = ini_get('max_execution_time');
+
+ // init directories to be checked when registered
+ foreach (array_column($this->genSteps, self::GEN_IDX_DEST_INFO) as $subDirs)
+ foreach ($subDirs as $sd)
+ $this->requiredDirs[] = $sd[0];
+
+ // fix genSteps 2 [icons] - no tiny inventory backgrounds
+ $this->genSteps[2][self::GEN_IDX_DEST_INFO] = array_slice($this->genSteps[2][self::GEN_IDX_DEST_INFO], 0, 3);
+
+ // fix genSteps 12 [pvp money icons] - smaller border offset for pvp currency icons
+ array_walk($this->genSteps[12][self::GEN_IDX_DEST_INFO], function(&$x) { $x[4] = 2; });
+
+ // fix genSteps 10 [holoday icons] - img src size is 90px
+ array_walk($this->genSteps[10][self::GEN_IDX_DEST_INFO], function(&$x) { $x[2] = 90; });
+ }
+
+ public function generate() : bool
+ {
+ // find out what to generate
+ $groups = [];
+ if (CLISetup::getOpt('icons'))
+ array_push($groups, 0, 2, 3, 4, 5, 10, 12);
+ if (CLISetup::getOpt('glyphs'))
+ $groups[] = 1;
+ if (CLISetup::getOpt('pagetexts'))
+ array_push($groups, 7, 8, 9);
+ if (CLISetup::getOpt('loadingscreens'))
+ $groups[] = 11;
+
+ if (!$groups) // by default do not generate loadingscreens
+ $groups = [0, 1, 2, 3, 4, 5, 7, 8, 9, 10, 12];
+
+ // removed unused generators and reset realPaths (in case of retry from failed attempt)
+ foreach ($this->genSteps as $idx => $_)
+ {
+ if (!in_array($idx, $groups))
+ unset($this->genSteps[$idx]);
+ else
+ $this->genSteps[$idx][self::GEN_IDX_SRC_REAL] = null;
+ }
+
+ if (!$this->checkSourceDirs())
+ {
+ CLI::write('[simpleimg] one or more required directories are missing:', CLI::LOG_ERROR);
+ return false;
+ }
+
+ sleep(2);
+
+ $allPaths = [];
+ foreach ($this->genSteps as $i => [, $path, , [$pattern, $isIcon, $tileSize], $outInfo])
+ {
+ $search = CLI::nicePath('', $path);
+ if ($pattern)
+ $search = '/'.strtr($search, ['\\' => '\\\\', '/' => '\\/']).$pattern.'/i';
+
+ $files = CLISetup::filesInPath($search, !!$pattern);
+ $allPaths = array_merge($allPaths, array_map(function ($x) { return substr($x, 0, -4); }, $files));
+
+ if (!$files)
+ {
+ CLI::write('[simpleimg] source directory "'.CLI::bold($search).'" does not contain files matching "'.CLI::bold($pattern), CLI::LOG_ERROR);
+ $this->success = false;
+ continue;
+ }
+
+ CLI::write('[simpleimg] processing '.count($files).' files in '.$path.'...');
+
+ $j = 0;
+ foreach ($files as $f)
+ {
+ ini_set('max_execution_time', $this->maxExecTime);
+
+ $src = null;
+ $na = explode(DIRECTORY_SEPARATOR, $f);
+ $img = explode('.', array_pop($na));
+ array_pop($img); // there are a hand full of images with multiple file endings or random dots in the name
+ $img = implode('.', $img);
+
+ if (!empty($this->cuNames[$i])) // file not from dbc -> name from array or skip file
+ {
+ if (!empty($this->cuNames[$i][strtolower($img)]))
+ $img = $this->cuNames[$i][strtolower($img)];
+ else if (!$tileSize)
+ {
+ $j += count($outInfo);
+ CLI::write('[simpleimg] skipping extraneous file '.$img.' (+'.count($outInfo).')');
+ continue;
+ }
+ }
+
+ $nFiles = count($outInfo) * ($tileSize ? array_sum(array_map('count', $this->cuNames[$i])) : count($files));
+
+ foreach ($outInfo as [$dest, $ext, $srcSize, $destSize, $borderOffset])
+ {
+ if ($tileSize)
+ {
+ foreach ($this->cuNames[$i] as $y => $row)
+ {
+ foreach ($row as $x => $name)
+ {
+ $j++;
+ $outFile = CLI::nicePath(($isIcon ? $this->fixIconName($name) : $name).'.'.$ext, $dest);
+
+ $this->status = ' - '.str_pad($j.'/'.$nFiles, 12).str_pad('('.number_format($j * 100 / $nFiles, 2).'%)', 9);
+
+ if (!CLISetup::getOpt('force') && file_exists($outFile))
+ {
+ CLI::write('[simpleimg] '.$this->status.' - file '.$outFile.' was already processed', CLI::LOG_BLANK, true, true);
+ continue;
+ }
+
+ if (!$src)
+ $src = $this->loadImageFile($f, $noSrcFile);
+
+ if (!$src) // error should be created by imagecreatefromblp
+ {
+ if (!$noSrcFile) // there are a couple of void file references in dbc, so this can't be a hard error.
+ $this->success = false;
+
+ continue;
+ }
+
+ /*
+ ready for some major bullshitery? well, here it comes anyway!
+ the class-icon tile [idx: 4] isn't 64x64 but 63x64 .. the right side border is 1px short
+ so if we don't watch out, the icons start to shift over and show the border
+ also the icon border is displaced by 1px
+ */
+ $from = array(
+ 'x' => $borderOffset + 1 + ($tileSize - ($i == 4 ? 1 : 0)) * $x,
+ 'y' => $borderOffset + 1 + $tileSize * $y,
+ 'w' => ($tileSize - ($i == 4 ? 1 : 0)) - $borderOffset * 2,
+ 'h' => $tileSize - $borderOffset * 2
+ );
+ $to = array(
+ 'x' => 0,
+ 'y' => 0,
+ 'w' => $destSize,
+ 'h' => $destSize
+ );
+
+ if (!$this->writeImageFile($src, $outFile, $from, $to))
+ $this->success = false;
+ }
+ }
+
+ // custom handle for combined icon 'quest_startend'
+ /* not used due to alphaChannel issues
+ if ($tileSize == 32)
+ {
+ $dest = imagecreatetruecolor(19, 16);
+ imagesavealpha($dest, true);
+ imagealphablending($dest, true);
+
+ // excalmationmark, questionmark
+ imagecopyresampled($dest, $src, 0, 1, 32 + 5, 32 + 2, 8, 15, 18, 30);
+ imagecopyresampled($dest, $src, 5, 0, 64 + 1, 32 + 1, 10, 16, 18, 28);
+
+ if (imagegif($dest, $dest.'quest_startend.gif'))
+ CLI::write(' extra - image '.$dest.'quest_startend.gif written', CLI::LOG_OK);
+ else
+ {
+ CLI::write(' extra - could not create image '.$dest.'quest_startend.gif', CLI::LOG_ERROR);
+ $this->success = false;
+ }
+
+ imagedestroy($dest);
+ }
+ */
+ }
+ else
+ {
+ $j++;
+ $this->status = ' - '.str_pad($j.'/'.$nFiles, 12).str_pad('('.number_format($j * 100 / $nFiles, 2).'%)', 9);
+ $outFile = CLI::nicePath(($isIcon ? $this->fixIconName($img) : $img).'.'.$ext, $dest);
+
+ if (!CLISetup::getOpt('force') && file_exists($outFile))
+ {
+ CLI::write('[simpleimg] '.$this->status.' - file '.$outFile.' was already processed', CLI::LOG_BLANK, true, true);
+ continue;
+ }
+
+ $src = $this->loadImageFile($f, $noSrcFile);
+ if (!$src) // error should be created by imagecreatefromblp
+ {
+ if (!$noSrcFile) // there are a couple of void file references in dbc, so this can't be a hard error.
+ $this->success = false;
+
+ continue;
+ }
+
+ $from = array(
+ 'x' => $borderOffset,
+ 'y' => $borderOffset,
+ 'w' => ($srcSize ?: imagesx($src)) - $borderOffset * 2,
+ 'h' => ($srcSize ?: imagesy($src)) - $borderOffset * 2
+ );
+ $to = array(
+ 'x' => 0,
+ 'y' => 0,
+ 'w' => $destSize ?: imagesx($src),
+ 'h' => $destSize ?: imagesy($src)
+ );
+
+ if (!$this->writeImageFile($src, $outFile, $from, $to))
+ $this->success = false;
+ }
+ }
+
+ unset($src);
+ }
+ }
+
+ // scan ItemDisplayInfo.dbc and SpellIcon.dbc for expected images and save them to an array
+ // load all icon paths into another array and xor these two
+ // excess entries for the directory are fine, excess entries for the dbc's are not
+ $dbcEntries = [];
+ $gens = array_keys($this->genSteps);
+
+ if (in_array(0, $gens)) // generates icons
+ {
+ if ($siRows = DB::Aowow()->selectCol('SELECT `iconPath` FROM dbc_spellicon WHERE `iconPath` NOT LIKE "%glyph-rune%"'))
+ foreach ($siRows as $icon)
+ if (stristr($icon, $this->genSteps[0][self::GEN_IDX_SRC_PATH])) // Icons/
+ $dbcEntries[] = strtolower($this->genSteps[0][self::GEN_IDX_SRC_REAL].substr(strrchr($icon, '\\'), 1));
+
+ if ($itemIcons = DB::Aowow()->selectCol('SELECT `inventoryIcon1` FROM dbc_itemdisplayinfo WHERE `inventoryIcon1` <> ""'))
+ foreach ($itemIcons as $icon)
+ $dbcEntries[] = strtolower($this->genSteps[0][self::GEN_IDX_SRC_REAL].DIRECTORY_SEPARATOR.$icon);
+ }
+
+ if (in_array(1, $gens)) // generates glyphs
+ if ($siRows = DB::Aowow()->selectCol('SELECT `iconPath` FROM dbc_spellicon WHERE `iconPath` LIKE "%glyph-rune%"'))
+ foreach ($siRows as $icon)
+ if (stristr($icon, $this->genSteps[1][self::GEN_IDX_SRC_PATH])) // Spellbook/
+ $dbcEntries[] = strtolower($this->genSteps[1][self::GEN_IDX_SRC_REAL].substr(strrchr($icon, '\\'), 1));
+
+ if (in_array(10, $gens)) // generates holiday icons
+ if ($eventIcons = DB::Aowow()->selectCol('SELECT `textureString` FROM dbc_holidays WHERE `textureString` <> ""'))
+ foreach ($eventIcons as $icon)
+ $dbcEntries[] = strtolower($this->genSteps[10][self::GEN_IDX_SRC_REAL].DIRECTORY_SEPARATOR.$icon.'start');
+
+ // case-insensitive array_unique *vomits silently into a corner*
+ $dbcEntries = array_intersect_key($dbcEntries, array_unique($dbcEntries));
+
+ if ($missing = array_diff(array_map('strtolower', $dbcEntries), array_map('strtolower', $allPaths)))
+ {
+ // hide affected icons from listviews
+ $iconNames = array_map(function($path) {
+ preg_match('/\/([^\/]+)\.blp$/i', $path, $m);
+ return $m ? $m[1] : null;
+ }, $missing);
+
+ DB::Aowow()->qry('UPDATE ::icons SET `cuFlags` = `cuFlags` | %i WHERE `name` IN %in', CUSTOM_EXCLUDE_FOR_LISTVIEW, $iconNames);
+
+ CLI::write('[simpleimg] the following '.count($missing).' images where referenced by DBC but not in the mpqData directory. They may need to be converted by hand later on.', CLI::LOG_WARN);
+ foreach ($missing as $m)
+ CLI::write(' - '.$m);
+ }
+
+ return $this->success;
+ }
+
+ private function fixIconName(string $name) : string
+ {
+ return preg_replace('/[^a-z0-9_-]/', '-', strtolower($name));
+ }
+});
+
+?>
diff --git a/setup/tools/filegen/soundfiles.func.php b/setup/tools/filegen/soundfiles.func.php
deleted file mode 100644
index 564b0938..00000000
--- a/setup/tools/filegen/soundfiles.func.php
+++ /dev/null
@@ -1,61 +0,0 @@
-selectCol('SELECT ABS(id) AS ARRAY_KEY, CONCAT(path, "/", `file`) FROM ?_sounds_files');
- $nFiles = count($files);
- $itr = $i = 0;
- $step = 1000;
- foreach ($files as $fileId => $filePath)
- {
- $i++;
- $itr++;
- if ($i == $step)
- {
- $i = 0;
- CLI::write(' - '.$itr.'/'.$nFiles.' ('.(intVal(100 * $itr / $nFiles).'%) done'));
- DB::Aowow()->selectCell('SELECT 1'); // keep mysql busy or it may go away
- }
-
- // expect converted files as file.wav_ or file.mp3_
- $filePath .= '_';
-
- // just use the first locale available .. there is no support for multiple audio files anyway
- foreach (CLISetup::$expectedPaths as $locStr => $__)
- {
- // get your paths straight!
- $p = CLI::nicePath($filePath, CLISetup::$srcDir, $locStr);
-
- if (CLISetup::fileExists($p))
- {
- // copy over to static/wowsounds/
- if (!copy($p, 'static/wowsounds/'.$fileId))
- {
- $ok = false;
- CLI::write(' - could not copy '.CLI::bold($p).' into '.CLI::bold('static/wowsounds/'.$fileId), CLI::LOG_ERROR);
- break 2;
- }
-
- continue 2;
- }
- }
-
- CLI::write(' - did not find file: '.CLI::bold(CLI::nicePath($filePath, CLISetup::$srcDir, '['.implode(',', CLISetup::$locales).']')), CLI::LOG_WARN);
- // flag as unusable in DB
- DB::Aowow()->query('UPDATE ?_sounds_files SET id = ?d WHERE ABS(id) = ?d', -$fileId, $fileId);
- }
-
- return $ok;
- }
-
-?>
-
diff --git a/setup/tools/filegen/soundfiles.ss.php b/setup/tools/filegen/soundfiles.ss.php
new file mode 100644
index 00000000..36ea6b4e
--- /dev/null
+++ b/setup/tools/filegen/soundfiles.ss.php
@@ -0,0 +1,73 @@
+ [[], CLISetup::ARGV_PARAM, 'Links converted sound files to database and moves them to destination.']
+ );
+
+ protected $requiredDirs = ['static/wowsounds/'];
+ protected $setupAfter = [['sounds'], []];
+
+ public function generate() : bool
+ {
+ // ALL files
+ $files = DB::Aowow()->selectCol('SELECT ABS(`id`) AS ARRAY_KEY, CONCAT(`path`, "/", `file`) FROM ::sounds_files');
+ $nFiles = count($files);
+ $qtLen = strlen($nFiles);
+ $sum = 0;
+ $time = new Timer(500);
+
+ foreach ($files as $fileId => $filePath)
+ {
+ $sum++;
+ if ($time->update())
+ {
+ CLI::write(sprintf('[soundfiles] * %'.$qtLen.'d / %d (%4.1f%%)', $sum, $nFiles, round(100 * $sum / $nFiles, 1)), CLI::LOG_BLANK, true, true);
+ DB::Aowow()->selectCell('SELECT 1'); // keep mysql busy or it may go away
+ }
+
+ // expect converted files as file.wav_ or file.mp3_
+ $filePath .= '_';
+
+ // just use the first locale available .. there is no support for multiple audio files for now
+ foreach (CLISetup::$locales as $loc)
+ {
+ foreach ($loc->gameDirs() as $dir)
+ {
+ // get your paths straight!
+ $p = CLI::nicePath($filePath, CLISetup::$srcDir, $dir);
+
+ if (!CLISetup::fileExists($p))
+ continue;
+
+ // copy over to static/wowsounds/
+ if (copy($p, 'static/wowsounds/'.$fileId))
+ continue 3;
+
+ $this->success = false;
+ CLI::write('[soundfiles] - could not copy '.CLI::bold($p).' into '.CLI::bold('static/wowsounds/'.$fileId), CLI::LOG_ERROR);
+ $time->reset();
+ }
+ }
+
+ CLI::write('[soundfiles] - did not find file: '.CLI::bold(CLI::nicePath($filePath, CLISetup::$srcDir, '['.implode(',', array_map(fn($x) => $x->json(), CLISetup::$locales)).']')), CLI::LOG_WARN);
+ $time->reset();
+ // flag as unusable in DB
+ DB::Aowow()->qry('UPDATE ::sounds_files SET `id` = %i WHERE ABS(`id`) = %i', -$fileId, $fileId);
+ }
+
+ return $this->success;
+ }
+});
+
+?>
diff --git a/setup/tools/filegen/spellscaling.ss.php b/setup/tools/filegen/spellscaling.ss.php
new file mode 100644
index 00000000..e91b80c5
--- /dev/null
+++ b/setup/tools/filegen/spellscaling.ss.php
@@ -0,0 +1,42 @@
+ [[], CLISetup::ARGV_PARAM, 'Compiles spell scaling data to file for spells with attribute SPELL_ATTR0_LEVEL_DAMAGE_CALCULATION.']
+ );
+
+ protected $fileTemplateDest = ['datasets/spell-scaling'];
+ protected $fileTemplateSrc = ['spell-scaling.in'];
+
+ protected $dbcSourceFiles = ['gtnpcmanacostscaler'];
+
+ private function debugify(array $data) : string
+ {
+ $buff = [];
+ foreach ($data as $id => $row)
+ $buff[] = str_pad($id, 7, " ", STR_PAD_LEFT).": ".$row;
+
+ return "{\r\n".implode(",\r\n", $buff)."\r\n}";
+ }
+
+ private function spellScalingSV() : string
+ {
+ $data = DB::Aowow()->selectCol('SELECT `idx` + 1 AS ARRAY_KEY, `factor` FROM dbc_gtnpcmanacostscaler');
+
+ return Cfg::get('DEBUG') ? $this->debugify($data) : Util::toJSON($data);
+ }
+})
+
+?>
diff --git a/setup/tools/filegen/statistics.func.php b/setup/tools/filegen/statistics.func.php
deleted file mode 100644
index b2ae6b9d..00000000
--- a/setup/tools/filegen/statistics.func.php
+++ /dev/null
@@ -1,210 +0,0 @@
- [[-20, 2, 0, 3], [-10, 0, 1, 1], null, 0.9560, 3.6640, 88.129021, 5, 47.003525, 5, [], []],
- 2 => [[-20, 2, 0, 3], [-10, 0, 1, 0], null, 0.9560, 3.4943, 88.129021, 5, 47.003525, 5, [], []],
- 3 => [[-20, 1, 1, 2], [-10, 0, 1, 2], null, 0.9880, -4.0873, 145.560408, 5, 145.560408, 0, [], []],
- 4 => [[-20, 1, 1, 2], [-10, 0, 1, 1], null, 0.9880, 2.0957, 145.560408, 5, 145.560408, 0, [], []],
- 5 => [[-10, 1, 0, 0], [-10, 0, 1, 0], null, 0.9830, 3.4178, 150.375940, 0, 0.0, 0, [], []],
- 6 => [[-20, 2, 0, 3], [-10, 0, 1, 0], null, 0.9560, 3.6640, 88.129021, 5, 47.003525, 0, [], ['parryrtng' => [0.25, 'percentOf', 'str']]], // Forcefull Deflection (49410)
- 7 => [[-20, 1, 1, 2], [-10, 0, 1, 0], null, 0.9880, 2.1080, 145.560408, 0, 145.560408, 5, [], []],
- 8 => [[-10, 1, 0, 0], [-10, 0, 1, 0], null, 0.9830, 3.6587, 150.375940, 0, 0.0, 0, [], []],
- 9 => [[-10, 1, 0, 0], [-10, 0, 1, 0], null, 0.9830, 2.4211, 150.375940, 0, 0.0, 0, [], []],
- 11 => [[-20, 2, 0, 0], [-10, 0, 1, 0], null, 0.9720, 5.6097, 116.890707, 0, 0.0, 0, [], []]
- );
-
- foreach ($dataz as $class => &$data)
- $data[2] = array_values(DB::Aowow()->selectRow('SELECT mle.chance*100 cMle, spl.chance*100 cSpl FROM dbc_gtchancetomeleecritbase mle, dbc_gtchancetospellcritbase spl WHERE mle.idx = spl.idx AND mle.idx = ?d', $class - 1));
-
- return $dataz;
- };
-
- $race = function()
- {
- // where did i get this data again..?
- // { str, agi, sta, int, spi, raceMod1, raceMod2 }
- $raceData = array(
- 1 => [20, 20, 20, 20, 20, [], []],
- 2 => [23, 17, 22, 17, 23, [], []],
- 3 => [22, 16, 23, 19, 19, [], []],
- 4 => [17, 25, 19, 20, 20, [], []],
- 5 => [19, 18, 21, 18, 25, [], []],
- 6 => [25, 15, 22, 15, 22, [], []],
- 7 => [15, 23, 19, 24, 20, [], []],
- 8 => [21, 22, 21, 16, 21, [], []],
- 10 => [17, 22, 18, 24, 19, [], []],
- 11 => [21, 17, 19, 21, 22, [], []]
- );
-
- $racials = new SpellList(array(['typeCat', -4], ['reqClassMask', 0]));
- $allMods = $racials->getProfilerMods();
- foreach ($allMods as $spellId => $mods)
- {
- if (!$mods)
- continue;
-
- // if there is ever a case where a racial is shared between races i don't want to know about it!
- $raceId = log($racials->getEntry($spellId)['reqRaceMask'], 2) + 1;
- if (!isset($raceData[$raceId]))
- continue;
-
- foreach ($mods as $jsonStat => $mod)
- {
- if (empty($raceData[$raceId][5][$jsonStat]))
- $raceData[$raceId][5][$jsonStat] = $mod;
- else
- $raceData[$raceId][6][$jsonStat] = $mod;
- }
- }
-
- return $raceData;
- };
-
- $combo = function()
- {
- $result = [];
- $critToDodge = array(
- 1 => 0.85/1.15, 2 => 1.00/1.15, 3 => 1.11/1.15,
- 4 => 2.00/1.15, 5 => 1.00/1.15, 6 => 0.85/1.15,
- 7 => 1.60/1.15, 8 => 1.00/1.15, 9 => 0.97/1.15, 11 => 2.00/1.15
- );
-
- // TrinityCore claims, DodgePerAgi per level and class can be constituted from critPerAgi (and level (and class))
- // who am i to argue
- // rebase stats to a specific race. chosen human as all stats are 20
- // level:{ str, agi, sta, int, spi, hp, mana, mleCrt%Agi, splCrt%Int, dodge%Agi, HealthRegenModToBaseStat, HealthRegenModToBonusStat }
-
- foreach ($critToDodge as $class => $mod)
- {
- // humans can't be hunter, shaman, druids (use tauren here)
- if (in_array($class, [3, 7, 11]))
- $offset = [25, 15, 22, 15, 22];
- else
- $offset = [20, 20, 20, 20, 20];
-
- $gtData = DB::Aowow()->select('
- SELECT mlecrt.idx - ?d AS ARRAY_KEY, mlecrt.chance * 100, splcrt.chance * 100, mlecrt.chance * 100 * ?f, baseHP5.ratio * 1, extraHP5.ratio * 1
- FROM dbc_gtchancetomeleecrit mlecrt
- JOIN dbc_gtchancetospellcrit splcrt ON splcrt.idx = mlecrt.idx
- JOIN dbc_gtoctregenhp baseHP5 ON baseHP5.idx = mlecrt.idx
- JOIN dbc_gtregenhpperspt extraHP5 ON extraHP5.idx = mlecrt.idx
- WHERE mlecrt.idx BETWEEN ?d AND ?d',
- (($class - 1) * 100) - 1, // class-offset
- $mod,
- (($class - 1) * 100) + 0, // lvl 1
- (($class - 1) * 100) + 79 // lvl 80
- );
-
- $rows = DB::World()->select('
- SELECT
- pls.level AS ARRAY_KEY,
- pls.str - ?d, pls.agi - ?d, pls.sta - ?d, pls.inte - ?d, pls.spi - ?d,
- pcls.basehp, IF(pcls.basemana <> 0, pcls.basemana, 100)
- FROM
- player_levelstats pls
- JOIN
- player_classlevelstats pcls ON pls.level = pcls.level AND pls.class = pcls.class
- WHERE
- pls.race = ?d AND pls.class = ?d ORDER BY pls.level ASC',
- $offset[0], $offset[1], $offset[2], $offset[3], $offset[4],
- in_array($class, [3, 7, 11]) ? 6 : 1,
- $class
- );
-
- $result[$class] = [];
- foreach ($rows as $lvl => $row)
- $result[$class][$lvl] = array_values(array_merge($row, $gtData[$lvl]));
- }
-
- return $result;
- };
-
- $level = function()
- {
- // base mana regeneration per level
- // identical across classes (just use one, that acutally has mana (offset: 100))
- // content of gtRegenMPPerSpt.dbc
-
- return DB::Aowow()->selectCol('SELECT idx-99 AS ARRAY_KEY, ratio FROM dbc_gtregenmpperspt WHERE idx >= 100 AND idx < 100 + ?d', MAX_LEVEL);
- };
-
- $skills = function()
- {
- // profession perks ... too lazy to formulate a search algorithm for two occurences
- return array(
- 186 => array( // mining / toughness
- 75 => ['sta' => 3],
- 150 => ['sta' => 5],
- 225 => ['sta' => 7],
- 300 => ['sta' => 10],
- 375 => ['sta' => 30],
- 450 => ['sta' => 60],
- ),
- 393 => array( // skinning / master of anatomy
- 75 => ['critstrkrtng' => 3],
- 150 => ['critstrkrtng' => 6],
- 225 => ['critstrkrtng' => 9],
- 300 => ['critstrkrtng' => 12],
- 375 => ['critstrkrtng' => 20],
- 450 => ['critstrkrtng' => 40],
- )
- );
- };
-
- $sub = ['classs', 'race', 'combo', 'level', 'skills'];
- $out = [];
- $success = true;
-
- foreach ($sub as $s)
- {
- $res = $$s();
- $out[$s] = $res;
- if (!$res)
- CLI::write('statistics - generator $'.$s.'() returned empty', CLI::LOG_WARN);
- }
-
- $toFile = 'g_statistics = '.preg_replace('/"\$([^$"]+)"/', '\1', Util::toJSON($out)).';';
-
- if (!CLISetup::writeFile('datasets/statistics', $toFile))
- $success = false;
-
- return $success;
- }
-
-?>
\ No newline at end of file
diff --git a/setup/tools/filegen/statistics.ss.php b/setup/tools/filegen/statistics.ss.php
new file mode 100644
index 00000000..3b0afb13
--- /dev/null
+++ b/setup/tools/filegen/statistics.ss.php
@@ -0,0 +1,211 @@
+ [[], CLISetup::ARGV_PARAM, 'Compiles player stats into file for the character profiler tool.']
+ );
+
+ protected $worldDependency = ['player_levelstats', 'player_classlevelstats'];
+ protected $dbcSourceFiles = ['gtchancetomeleecrit', 'gtchancetomeleecritbase', 'gtchancetospellcrit', 'gtchancetospellcritbase', 'gtoctregenhp', 'gtregenmpperspt', 'gtregenhpperspt'];
+ protected $requiredDirs = ['datasets/'];
+
+ public function generate() : bool
+ {
+ $sub = ['classs', 'race', 'combo', 'level', 'skills'];
+ $out = [];
+
+ foreach ($sub as $s)
+ {
+ if ($out[$s] = $this->$s())
+ continue;
+
+ CLI::write('[statistics] generator '.$s.'() returned empty', CLI::LOG_WARN);
+ $this->success = false;
+ }
+
+ $toFile = 'g_statistics = '.preg_replace('/"\$([^$"]+)"/', '\1', Util::toJSON($out)).';';
+
+ if (!CLISetup::writeFile('datasets/statistics', $toFile))
+ $this->success = false;
+
+ return $this->success;
+ }
+
+ // constants and mods taken from TrinityCore (Player.cpp, StatSystem.cpp)
+ private function classs() : array
+ {
+ /* content per Index
+ mleatkpwr[base, strMultiplier, agiMultiplier, levelMultiplier]
+ rngatkpwr[base, strMultiplier, agiMultiplier, levelMultiplier]
+ baseCritPct[phys, spell]
+ diminishingConstant
+ baseDodgePct
+ DodgeCap
+ baseParryPct
+ ParryCap
+ baseBlockPct
+ classMod1 applies mod directly only one class having something worth mentioning: DK
+ classMod2 applies mod directly so what were they originally used for..?
+ */
+
+ $dataz = array(
+ 1 => [[-20, 2, 0, 3], [-10, 0, 1, 1], null, 0.9560, 3.6640, 88.129021, 5, 47.003525, 5, [], []],
+ 2 => [[-20, 2, 0, 3], [-10, 0, 1, 0], null, 0.9560, 3.4943, 88.129021, 5, 47.003525, 5, [], []],
+ 3 => [[-20, 1, 1, 2], [-10, 0, 1, 2], null, 0.9880, -4.0873, 145.560408, 5, 145.560408, 0, [], []],
+ 4 => [[-20, 1, 1, 2], [-10, 0, 1, 1], null, 0.9880, 2.0957, 145.560408, 5, 145.560408, 0, [], []],
+ 5 => [[-10, 1, 0, 0], [-10, 0, 1, 0], null, 0.9830, 3.4178, 150.375940, 0, 0.0, 0, [], []],
+ 6 => [[-20, 2, 0, 3], [-10, 0, 1, 0], null, 0.9560, 3.6640, 88.129021, 5, 47.003525, 0, [], ['parryrtng' => [0.25, 'percentOf', 'str']]], // Forcefull Deflection (49410)
+ 7 => [[-20, 1, 1, 2], [-10, 0, 1, 0], null, 0.9880, 2.1080, 145.560408, 0, 145.560408, 5, [], []],
+ 8 => [[-10, 1, 0, 0], [-10, 0, 1, 0], null, 0.9830, 3.6587, 150.375940, 0, 0.0, 0, [], []],
+ 9 => [[-10, 1, 0, 0], [-10, 0, 1, 0], null, 0.9830, 2.4211, 150.375940, 0, 0.0, 0, [], []],
+ 11 => [[-20, 2, 0, 0], [-10, 0, 1, 0], null, 0.9720, 5.6097, 116.890707, 0, 0.0, 0, [], []]
+ );
+
+ foreach ($dataz as $class => &$data)
+ $data[2] = array_values(DB::Aowow()->selectRow('SELECT mle.chance*100 cMle, spl.chance*100 cSpl FROM dbc_gtchancetomeleecritbase mle, dbc_gtchancetospellcritbase spl WHERE mle.idx = spl.idx AND mle.idx = %i', $class - 1));
+
+ return $dataz;
+ }
+
+ // { str, agi, sta, int, spi, raceMod1, raceMod2 }
+ private function race() : array
+ {
+ $raceData = DB::World()->selectAssoc('SELECT `race` AS ARRAY_KEY, MIN(`str`), MIN(`agi`), MIN(`sta`), MIN(`inte`), MIN(`spi`) FROM player_levelstats WHERE `level` = 1 GROUP BY `race` ORDER BY `race` ASC');
+ foreach ($raceData as &$rd)
+ $rd = array_values($rd + [[], []]);
+
+ $racials = new SpellList(array(['typeCat', -4], ['reqClassMask', 0]));
+ $allMods = $racials->getProfilerMods();
+ foreach ($allMods as $spellId => $mods)
+ {
+ if (!$mods)
+ continue;
+
+ // if there is ever a case where a racial is shared between races i don't want to know about it!
+ $raceId = log($racials->getEntry($spellId)['reqRaceMask'], 2) + 1;
+ if (!isset($raceData[$raceId]))
+ continue;
+
+ foreach ($mods as $jsonStat => $mod)
+ {
+ if (empty($raceData[$raceId][5][$jsonStat]))
+ $raceData[$raceId][5][$jsonStat] = $mod;
+ else
+ $raceData[$raceId][6][$jsonStat] = $mod;
+ }
+ }
+
+ return $raceData;
+ }
+
+ // TrinityCore claims, DodgePerAgi per level and class can be constituted from critPerAgi (and level (and class))
+ // who am i to argue
+ // rebase stats to a specific race. chosen human as all stats are 20 and tauren for hunter, shaman and druid
+ // level:{ str, agi, sta, int, spi, hp, mana, mleCrt%Agi, splCrt%Int, dodge%Agi, HealthRegenModToBaseStat, HealthRegenModToBonusStat }
+ private function combo() : array
+ {
+ $result = [];
+ $critToDodge = array(
+ 1 => 0.85/1.15, 2 => 1.00/1.15, 3 => 1.11/1.15,
+ 4 => 2.00/1.15, 5 => 1.00/1.15, 6 => 0.85/1.15,
+ 7 => 1.60/1.15, 8 => 1.00/1.15, 9 => 0.97/1.15, 11 => 2.00/1.15
+ );
+
+ foreach ($critToDodge as $class => $mod)
+ {
+ // humans can't be hunter, shaman, druids (use tauren here)
+ if (in_array($class, [3, 7, 11]))
+ $offset = array_values(DB::World()->selectRow('SELECT MIN(`str`), MIN(`agi`), MIN(`sta`), MIN(`inte`), MIN(`spi`) FROM player_levelstats WHERE `level` = 1 AND `race` = 6'));
+ else
+ $offset = array_values(DB::World()->selectRow('SELECT MIN(`str`), MIN(`agi`), MIN(`sta`), MIN(`inte`), MIN(`spi`) FROM player_levelstats WHERE `level` = 1 AND `race` = 1'));
+
+ $gtData = DB::Aowow()->selectAssoc(
+ 'SELECT mlecrt.idx - %i AS ARRAY_KEY, mlecrt.chance * 100, splcrt.chance * 100, mlecrt.chance * 100 * %f, baseHP5.ratio * 1, extraHP5.ratio * 1
+ FROM dbc_gtchancetomeleecrit mlecrt
+ JOIN dbc_gtchancetospellcrit splcrt ON splcrt.idx = mlecrt.idx
+ JOIN dbc_gtoctregenhp baseHP5 ON baseHP5.idx = mlecrt.idx
+ JOIN dbc_gtregenhpperspt extraHP5 ON extraHP5.idx = mlecrt.idx
+ WHERE mlecrt.idx BETWEEN %i AND %i',
+ (($class - 1) * 100) - 1, // class-offset
+ $mod,
+ (($class - 1) * 100) + 0, // lvl 1
+ (($class - 1) * 100) + 79 // lvl 80
+ );
+
+ $rows = DB::World()->selectAssoc(
+ 'SELECT pls.level AS ARRAY_KEY,
+ pls.str - %i, pls.agi - %i, pls.sta - %i, pls.inte - %i, pls.spi - %i,
+ pcls.basehp, IF(pcls.basemana <> 0, pcls.basemana, 100)
+ FROM player_levelstats pls
+ JOIN player_classlevelstats pcls ON pls.level = pcls.level AND pls.class = pcls.class
+ WHERE pls.race = %i AND
+ pls.class = %i
+ ORDER BY pls.level ASC',
+ $offset[0], $offset[1], $offset[2], $offset[3], $offset[4],
+ in_array($class, [3, 7, 11]) ? 6 : 1,
+ $class
+ );
+
+ $result[$class] = [];
+ foreach ($rows as $lvl => $row)
+ $result[$class][$lvl] = array_values(array_merge($row, $gtData[$lvl]));
+ }
+
+ return $result;
+ }
+
+ // base mana regeneration per level
+ // identical across classes (just use one, that acutally has mana (offset: 100))
+ // content of gtRegenMPPerSpt.dbc
+ private function level() : array
+ {
+ return DB::Aowow()->selectCol('SELECT idx-99 AS ARRAY_KEY, ratio FROM dbc_gtregenmpperspt WHERE idx >= 100 AND idx < 100 + %i', MAX_LEVEL);
+ }
+
+ // profession perks ... too lazy to formulate a search algorithm for two occurences
+ private function skills() : array
+ {
+ // DB::Aowow()->selectAssoc(
+ // 'SELECT sk.id AS "skillId", sla.reqSkillLevel, s.effect1AuraId AS "auraId", s.effect1MiscValue, s.effect1BasePoints + s.effect1DieSides AS "qty"
+ // FROM dbc_skilllineability sla
+ // JOIN dbc_skillline sk ON sk.id = sla.skilllineid
+ // JOIN dbc_spell s ON s.id = sla.spellId
+ // WHERE sla.acquiremethod = 1 AND // learn on skillup
+ // sk.categoryId = 11 AND // primary profession
+ // s.effect1Id = 6 AND // ApplyAura
+ // s.effect1AuraId IN (29, 189) // ModStatFlat, ModRating (need more?)
+ // ');
+
+ return array(
+ SKILL_MINING => array( // mining / toughness
+ 75 => ['sta' => 3],
+ 150 => ['sta' => 5],
+ 225 => ['sta' => 7],
+ 300 => ['sta' => 10],
+ 375 => ['sta' => 30],
+ 450 => ['sta' => 60],
+ ),
+ SKILL_SKINNING => array( // skinning / master of anatomy
+ 75 => ['critstrkrtng' => 3],
+ 150 => ['critstrkrtng' => 6],
+ 225 => ['critstrkrtng' => 9],
+ 300 => ['critstrkrtng' => 12],
+ 375 => ['critstrkrtng' => 20],
+ 450 => ['critstrkrtng' => 40],
+ )
+ );
+ }
+});
+
+?>
diff --git a/setup/tools/filegen/talentCalc.func.php b/setup/tools/filegen/talentCalc.func.php
deleted file mode 100644
index 10e6d68d..00000000
--- a/setup/tools/filegen/talentCalc.func.php
+++ /dev/null
@@ -1,211 +0,0 @@
-getProfilerMods();
-
- $buildTree = function ($class) use (&$petFamIcons, &$tSpells, $spellMods)
- {
- $petCategories = [];
-
- $mask = $class ? 1 << ($class - 1) : 0;
-
- // All "tabs" of a given class talent
- $tabs = DB::Aowow()->select('SELECT * FROM dbc_talenttab WHERE classMask = ?d ORDER BY `tabNumber`, `creatureFamilyMask`', $mask);
- $result = [];
-
- for ($tabIdx = 0; $tabIdx < count($tabs); $tabIdx++)
- {
- $talents = DB::Aowow()->select('SELECT t.id AS tId, t.*, s.name_loc0, s.name_loc2, s.name_loc3, s.name_loc4, s.name_loc6, s.name_loc8, LOWER(SUBSTRING_INDEX(si.iconPath, "\\\\", -1)) AS iconString FROM dbc_talent t, dbc_spell s, dbc_spellicon si WHERE si.`id` = s.`iconId` AND t.`tabId`= ?d AND s.`id` = t.`rank1` ORDER by t.`row`, t.`column`', $tabs[$tabIdx]['id']);
- $result[$tabIdx] = array(
- 'n' => Util::localizedString($tabs[$tabIdx], 'name'),
- 't' => []
- );
-
- if (!$class)
- {
- $petFamId = log($tabs[$tabIdx]['creatureFamilyMask'], 2);
- $result[$tabIdx]['icon'] = $petFamIcons[$petFamId];
- $petCategories = DB::Aowow()->SelectCol('SELECT id AS ARRAY_KEY, categoryEnumID FROM dbc_creaturefamily WHERE petTalentType = ?d', $petFamId);
- $result[$tabIdx]['f'] = array_keys($petCategories);
- }
-
- // talent dependencies go here
- $depLinks = [];
- $tNums = [];
-
- for ($talentIdx = 0; $talentIdx < count($talents); $talentIdx++)
- {
- $tNums[$talents[$talentIdx]['tId']] = $talentIdx;
-
- $d = [];
- $s = [];
- $i = $talents[$talentIdx]['tId'];
- $n = Util::localizedString($talents[$talentIdx], 'name');
- $x = $talents[$talentIdx]['column'];
- $y = $talents[$talentIdx]['row'];
- $r = null;
- $t = [];
- $j = [];
- $icon = $talents[$talentIdx]['iconString'];
- $m = $talents[$talentIdx]['rank2'] == 0 ? 1 : (
- $talents[$talentIdx]['rank3'] == 0 ? 2 : (
- $talents[$talentIdx]['rank4'] == 0 ? 3 : (
- $talents[$talentIdx]['rank5'] == 0 ? 4 : 5
- )
- )
- );
-
- // duplet handling
- $f = [];
- foreach ($petCategories as $k => $v)
- {
- // cant handle 64bit integer .. split
- if ($v >= 32 && ((1 << ($v - 32)) & $talents[$talentIdx]['petCategory2']))
- $f[] = $k;
- else if ($v < 32 && ((1 << $v) & $talents[$talentIdx]['petCategory1']))
- $f[] = $k;
- }
-
- for ($itr = 0; $itr <= ($m - 1); $itr++)
- {
- if (!$tSpells->getEntry($talents[$talentIdx]['rank'.($itr + 1)]))
- continue;
-
- $d[] = $tSpells->parseText()[0];
- $s[] = $talents[$talentIdx]['rank'.($itr + 1)];
- if (isset($spellMods[$talents[$talentIdx]['rank'.($itr + 1)]]))
- $j[] = $spellMods[$talents[$talentIdx]['rank'.($itr + 1)]];
- else
- $j[] = null;
-
- if ($talents[$talentIdx]['talentSpell'])
- $t[] = $tSpells->getTalentHeadForCurrent();
- }
-
- if ($talents[$talentIdx]['reqTalent'])
- {
- // we didn't encounter the required talent yet => create reference
- if (!isset($tNums[$talents[$talentIdx]['reqTalent']]))
- $depLinks[$talents[$talentIdx]['reqTalent']] = $talentIdx;
-
- $r = [$tNums[$talents[$talentIdx]['reqTalent']] ?? 0, $talents[$talentIdx]['reqRank'] + 1];
- }
-
- $result[$tabIdx]['t'][$talentIdx] = array(
- 'i' => $i,
- 'n' => $n,
- 'm' => $m,
- 'd' => $d,
- 's' => $s,
- 'x' => $x,
- 'y' => $y,
- 'j' => $j
- );
-
- if (isset($r))
- $result[$tabIdx]['t'][$talentIdx]['r'] = $r;
-
- if (!empty($t))
- $result[$tabIdx]['t'][$talentIdx]['t'] = $t;
-
- if (!empty($f))
- $result[$tabIdx]['t'][$talentIdx]['f'] = $f;
-
- if ($class)
- $result[$tabIdx]['t'][$talentIdx]['iconname'] = $icon;
-
- // If this talent is a reference, add it to the array of talent dependencies
- if (isset($depLinks[$talents[$talentIdx]['tId']]))
- {
- $result[$tabIdx]['t'][$depLinks[$talents[$talentIdx]['tId']]]['r'][0] = $talentIdx;
- unset($depLinks[$talents[$talentIdx]['tId']]);
- }
- }
-
- // Remove all dependencies for which the talent has not been found
- foreach ($depLinks as $dep_link)
- unset($result[$tabIdx]['t'][$dep_link]['r']);
- }
-
- return $result;
- };
-
- // my neighbour is noisy as fuck and my head hurts, so ..
- $petFamIcons = ['Ability_Druid_KingoftheJungle', 'Ability_Druid_DemoralizingRoar', 'Ability_EyeOfTheOwl']; // .. i've no idea where to fetch these from
- $classes = [CLASS_WARRIOR, CLASS_PALADIN, CLASS_HUNTER, CLASS_ROGUE, CLASS_PRIEST, CLASS_DEATHKNIGHT, CLASS_SHAMAN, CLASS_MAGE, CLASS_WARLOCK, CLASS_DRUID];
- $petIcons = '';
-
- // check directory-structure
- foreach (Util::$localeStrings as $dir)
- if (!CLISetup::writeDir('datasets/'.$dir))
- $success = false;
-
- $tSpellIds = DB::Aowow()->selectCol('SELECT rank1 FROM dbc_talent UNION SELECT rank2 FROM dbc_talent UNION SELECT rank3 FROM dbc_talent UNION SELECT rank4 FROM dbc_talent UNION SELECT rank5 FROM dbc_talent');
- $tSpells = new SpellList(array(['s.id', $tSpellIds], CFG_SQL_LIMIT_NONE));
-
- foreach (CLISetup::$localeIds as $lId)
- {
- User::useLocale($lId);
- Lang::load(Util::$localeStrings[$lId]);
-
- // TalentCalc
- foreach ($classes as $cMask)
- {
- set_time_limit(20);
-
- $cId = log($cMask, 2) + 1;
- $file = 'datasets/'.User::$localeString.'/talents-'.$cId;
- $toFile = '$WowheadTalentCalculator.registerClass('.$cId.', '.Util::toJSON($buildTree($cId)).')';
-
- if (!CLISetup::writeFile($file, $toFile))
- $success = false;
- }
-
- // PetCalc
- if (empty($petIcons))
- {
- $pets = DB::Aowow()->SelectCol('SELECT id AS ARRAY_KEY, LOWER(SUBSTRING_INDEX(iconString, "\\\\", -1)) AS iconString FROM dbc_creaturefamily WHERE petTalentType IN (0, 1, 2)');
- $petIcons = Util::toJSON($pets);
- }
-
- $toFile = "var g_pet_icons = ".$petIcons.";\n\n";
- $toFile .= 'var g_pet_talents = '.Util::toJSON($buildTree(0)).';';
- $file = 'datasets/'.User::$localeString.'/pet-talents';
-
- if (!CLISetup::writeFile($file, $toFile))
- $success = false;
- }
-
- return $success;
- }
-?>
diff --git a/setup/tools/filegen/talentIcons.func.php b/setup/tools/filegen/talentIcons.func.php
deleted file mode 100644
index 168137b2..00000000
--- a/setup/tools/filegen/talentIcons.func.php
+++ /dev/null
@@ -1,102 +0,0 @@
- $v)
- {
- if (!$v)
- continue;
-
- set_time_limit(10);
-
- for ($tree = 0; $tree < 3; $tree++)
- {
- $what = $k ? 'classMask' : 'creatureFamilyMask';
- $set = $k ? 1 << ($k - 1) : 1 << $tree;
- $subset = $k ? $tree : 0;
- $path = $k ? 'talents/icons' : 'hunterpettalents';
- $outFile = 'static/images/wow/'.$path.'/'.$v.'_'.($tree + 1).'.jpg';
- $icons = DB::Aowow()->SelectCol($query, $what, $set, $subset);
-
- if (empty($icons))
- {
- CLI::write('talentIcons - query for '.$v.' tree: '.$k.' returned empty', CLI::LOG_ERROR);
- $success = false;
- continue;
- }
-
- if ($res = imageCreateTrueColor(count($icons) * $dims, 2 * $dims))
- {
- for ($i = 0; $i < count($icons); $i++)
- {
- $imgFile = 'static/images/wow/icons/medium/'.strtolower($icons[$i]).'.jpg';
- if (!file_exists($imgFile))
- {
- CLI::write('talentIcons - raw image '.CLI::bold($imgFile). ' not found', CLI::LOG_ERROR);
- $success = false;
- break;
- }
-
- $im = imagecreatefromjpeg($imgFile);
-
- // colored
- imagecopymerge($res, $im, $i * $dims, 0, 0, 0, imageSX($im), imageSY($im), 100);
-
- // grayscale
- if (imageistruecolor($im))
- imagetruecolortopalette($im, false, 256);
-
- for ($j = 0; $j < imagecolorstotal($im); $j++)
- {
- $color = imagecolorsforindex($im, $j);
- $gray = round(0.299 * $color['red'] + 0.587 * $color['green'] + 0.114 * $color['blue']);
- imagecolorset($im, $j, $gray, $gray, $gray);
- }
- imagecopymerge($res, $im, $i * $dims, $dims, 0, 0, imageSX($im), imageSY($im), 100);
- }
-
- if (@imagejpeg($res, $outFile))
- CLI::write(sprintf(ERR_NONE, CLI::bold($outFile)), CLI::LOG_OK);
- else
- {
- $success = false;
- CLI::write('talentIcons - '.CLI::bold($outFile.'.jpg').' could not be written', CLI::LOG_ERROR);
- }
- }
- else
- {
- $success = false;
- CLI::write('talentIcons - image resource not created', CLI::LOG_ERROR);
- continue;
- }
- }
- }
-
- return $success;
- }
-
-?>
diff --git a/setup/tools/filegen/talentcalc.ss.php b/setup/tools/filegen/talentcalc.ss.php
new file mode 100644
index 00000000..1207fdf4
--- /dev/null
+++ b/setup/tools/filegen/talentcalc.ss.php
@@ -0,0 +1,199 @@
+ [[], CLISetup::ARGV_PARAM, 'Compiles talent tree data to file for the talent calculator tool.']
+ );
+
+ protected $dbcSourceFiles = ['talenttab', 'talent', 'spell', 'creaturefamily', 'spellicon'];
+ protected $setupAfter = [['spell'], []];
+ protected $requiredDirs = ['datasets/'];
+ protected $localized = true;
+
+ private $petFamIcons = [];
+ private $tSpells = null;
+ private $spellMods = [];
+
+ public function generate() : bool
+ {
+ // target direcotries are missing
+ if (!$this->success)
+ return false;
+
+ // my neighbour is noisy as fuck and my head hurts, so ..
+ $this->petFamIcons = ['Ability_Druid_KingoftheJungle', 'Ability_Druid_DemoralizingRoar', 'Ability_EyeOfTheOwl']; // .. i've no idea where to fetch these from
+ $this->spellMods = (new SpellList(array(['typeCat', -2])))->getProfilerMods();
+
+ $petIcons = Util::toJSON(DB::Aowow()->SelectCol('SELECT `id` AS ARRAY_KEY, LOWER(SUBSTRING_INDEX(`iconString`, "\\", -1)) AS "iconString" FROM dbc_creaturefamily WHERE `petTalentType` IN (0, 1, 2)'));
+
+ $tSpellIds = DB::Aowow()->selectCol('SELECT `rank1` FROM dbc_talent UNION SELECT `rank2` FROM dbc_talent UNION SELECT `rank3` FROM dbc_talent UNION SELECT `rank4` FROM dbc_talent UNION SELECT `rank5` FROM dbc_talent');
+ $this->tSpells = new SpellList(array(['s.id', $tSpellIds]));
+
+ foreach (CLISetup::$locales as $loc)
+ {
+ Lang::load($loc);
+
+ // TalentCalc
+ foreach (ChrClass::cases() as $class)
+ {
+ set_time_limit(20);
+
+ $file = 'datasets/'.$loc->json().'/talents-'.$class->value;
+ $toFile = '$WowheadTalentCalculator.registerClass('.$class->value.', '.Util::toJSON($this->buildTree($class->toMask())).')';
+
+ if (!CLISetup::writeFile($file, $toFile))
+ $this->success = false;
+ }
+
+ // PetCalc
+ $toFile = "var g_pet_icons = ".$petIcons.";\n\n";
+ $toFile .= 'var g_pet_talents = '.Util::toJSON($this->buildTree(0)).';';
+ $file = 'datasets/'.$loc->json().'/pet-talents';
+
+ if (!CLISetup::writeFile($file, $toFile))
+ $this->success = false;
+ }
+
+ return $this->success;
+ }
+
+ private function buildTree(int $classMask) : array
+ {
+ $petCategories = [];
+
+ // All "tabs" of a given class talent
+ $tabs = DB::Aowow()->selectAssoc('SELECT * FROM dbc_talenttab WHERE `classMask` = %i ORDER BY `tabNumber`, `creatureFamilyMask`', $classMask);
+ $result = [];
+
+ for ($tabIdx = 0; $tabIdx < count($tabs); $tabIdx++)
+ {
+ $talents = DB::Aowow()->selectAssoc(
+ 'SELECT t.id AS "tId", t.*, IF(t.rank5, 5, IF(t.rank4, 4, IF(t.rank3, 3, IF(t.rank2, 2, 1)))) AS "maxRank",
+ s.`name_loc0`, s.`name_loc2`, s.`name_loc3`, s.`name_loc4`, s.`name_loc6`, s.`name_loc8`,
+ LOWER(SUBSTRING_INDEX(si.`iconPath`, "\\", -1)) AS "iconString"
+ FROM dbc_talent t, dbc_spell s, dbc_spellicon si
+ WHERE si.`id` = s.`iconId` AND t.`tabId`= %i AND s.`id` = t.`rank1`
+ ORDER BY t.`row`, t.`column`, t.`id` ASC',
+ $tabs[$tabIdx]['id']
+ );
+
+ $result[$tabIdx] = array(
+ 'n' => Util::localizedString($tabs[$tabIdx], 'name'),
+ 't' => []
+ );
+
+ if (!$classMask)
+ {
+ $petFamId = log($tabs[$tabIdx]['creatureFamilyMask'], 2);
+ $result[$tabIdx]['icon'] = $this->petFamIcons[$petFamId];
+ $petCategories = DB::Aowow()->SelectCol('SELECT `id` AS ARRAY_KEY, `categoryEnumID` FROM dbc_creaturefamily WHERE `petTalentType` = %i', $petFamId);
+ $result[$tabIdx]['f'] = array_keys($petCategories);
+ }
+
+ // talent dependencies go here
+ $depLinks = [];
+ $tNums = [];
+
+ for ($talentIdx = 0; $talentIdx < count($talents); $talentIdx++)
+ {
+ $tNums[$talents[$talentIdx]['tId']] = $talentIdx;
+
+ $talent = array(
+ 'i' => $talents[$talentIdx]['tId'], // talent id
+ 'n' => Util::localizedString($talents[$talentIdx], 'name'), // talent name
+ 'm' => $talents[$talentIdx]['maxRank'], // maxRank
+ 'd' => [], // [descriptions]
+ 's' => [], // [spellIds]
+ 'x' => $talents[$talentIdx]['column'], // col #
+ 'y' => $talents[$talentIdx]['row'], // row #
+ 'j' => [] // [spellMods] that are applied when used in profiler
+ // 'r' => [] // [reqTalentId, reqRank] (can be omitted)
+ // 't' => [] // talentspell tooltip (can be omitted)
+ // 'f' => [] // [petFamilyIds] (can be omitted)
+ );
+
+ if ($classMask)
+ $talent['iconname'] = $talents[$talentIdx]['iconString'];
+
+ for ($itr = 0; $itr <= ($talent['m'] - 1); $itr++)
+ {
+ $spell = $talents[$talentIdx]['rank'.($itr + 1)];
+ if (!$this->tSpells->getEntry($spell))
+ continue;
+
+ $talent['d'][] = $this->tSpells->parseText()[0];
+ $talent['s'][] = $talents[$talentIdx]['rank'.($itr + 1)];
+
+ if ($classMask && isset($this->spellMods[$spell]))
+ if ($mod = $this->spellMods[$spell])
+ $talent['j'][] = $mod;
+
+ if ($talents[$talentIdx]['talentSpell'])
+ $talent['t'][] = $this->tSpells->getTalentHeadForCurrent();
+ }
+
+ foreach ($petCategories as $k => $v)
+ {
+ // cant handle 64bit integer .. split
+ if ($v >= 32 && ((1 << ($v - 32)) & $talents[$talentIdx]['petCategory2']))
+ $talent['f'][] = $k;
+ else if ($v < 32 && ((1 << $v) & $talents[$talentIdx]['petCategory1']))
+ $talent['f'][] = $k;
+ }
+
+ if ($talents[$talentIdx]['reqTalent'])
+ {
+ // we didn't encounter the required talent yet => create reference
+ if (!isset($tNums[$talents[$talentIdx]['reqTalent']]))
+ $depLinks[$talents[$talentIdx]['reqTalent']] = $talentIdx;
+
+ $talent['r'] = [$tNums[$talents[$talentIdx]['reqTalent']] ?? 0, $talents[$talentIdx]['reqRank'] + 1];
+ }
+
+ $result[$tabIdx]['t'][$talentIdx] = $talent;
+
+ // If this talent is a reference, add it to the array of talent dependencies
+ if (isset($depLinks[$talents[$talentIdx]['tId']]))
+ {
+ $result[$tabIdx]['t'][$depLinks[$talents[$talentIdx]['tId']]]['r'][0] = $talentIdx;
+ unset($depLinks[$talents[$talentIdx]['tId']]);
+ }
+ }
+
+ // Remove all dependencies for which the talent has not been found
+ foreach ($depLinks as $dep_link)
+ unset($result[$tabIdx]['t'][$dep_link]['r']);
+ }
+
+ return $result;
+ }
+});
+
+?>
diff --git a/setup/tools/filegen/talenticons.ss.php b/setup/tools/filegen/talenticons.ss.php
new file mode 100644
index 00000000..efbdfdbe
--- /dev/null
+++ b/setup/tools/filegen/talenticons.ss.php
@@ -0,0 +1,135 @@
+ [[], CLISetup::ARGV_PARAM, 'Generates icon textures for the talent calculator tool.']
+ );
+
+ protected $dbcSourceFiles = ['talenttab', 'talent', 'spell'];
+ protected $setupAfter = [['icons', 'spell'], ['simpleimg']];
+ protected $requiredDirs = ['static/images/wow/talents/icons', 'static/images/wow/hunterpettalents'];
+
+ private const ICON_SIZE = 36; // px
+
+ public function generate() : bool
+ {
+ /***************/
+ /* Hunter Pets */
+ /***************/
+
+ for ($tabIdx = 0; $tabIdx < 3; $tabIdx++)
+ {
+ $outFile = 'static/images/wow/hunterpettalents/icons_'.($tabIdx + 1).'.jpg';
+
+ if ($tex = $this->compileTexture('creatureFamilyMask', 1 << $tabIdx, 0))
+ {
+ if (!imagejpeg($tex, $outFile))
+ {
+ CLI::write('[talenticons] - '.CLI::bold($outFile.'.jpg').' could not be written', CLI::LOG_ERROR);
+ $this->success = false;
+ }
+ else
+ CLI::write('[talenticons] created file '.CLI::bold($outFile), CLI::LOG_OK, true, true);
+ }
+ else
+ $this->success = false;
+ }
+
+
+ /***********/
+ /* Players */
+ /***********/
+
+ foreach (ChrClass::cases() as $class)
+ {
+ set_time_limit(10);
+
+ for ($tabIdx = 0; $tabIdx < 3; $tabIdx++)
+ {
+ $outFile = 'static/images/wow/talents/icons/'.$class->json().'_'.($tabIdx + 1).'.jpg';
+
+ if ($tex = $this->compileTexture('classMask', $class->toMask(), $tabIdx))
+ {
+ if (!imagejpeg($tex, $outFile))
+ {
+ CLI::write('[talenticons] - '.CLI::bold($outFile.'.jpg').' could not be written', CLI::LOG_ERROR);
+ $this->success = false;
+ }
+ else
+ CLI::write('[talenticons] created file '.CLI::bold($outFile), CLI::LOG_OK, true, true);
+ }
+ else
+ $this->success = false;
+ }
+ }
+
+ return $this->success;
+ }
+
+ private function compileTexture(string $ttField, int $searchMask, int $tabIdx) : ?\GdImage
+ {
+ $icons = DB::Aowow()->SelectCol(
+ 'SELECT ic.`name`
+ FROM ::icons ic
+ JOIN ::spell s ON s.`iconId` = ic.`id`
+ JOIN dbc_talent t ON t.`rank1` = s.`id`
+ JOIN dbc_talenttab tt ON tt.`id` = t.`tabId`
+ WHERE tt.%n = %i AND tt.`tabNumber` = %i
+ ORDER BY t.`row`, t.`column`, t.`id` ASC',
+ $ttField, $searchMask, $tabIdx);
+
+ if (empty($icons))
+ {
+ CLI::write('[talenticons] - query for '.$ttField.': '.$searchMask.' on idx: '.$tabIdx.' returned empty', CLI::LOG_ERROR);
+ return null;
+ }
+
+ $res = imageCreateTrueColor(count($icons) * self::ICON_SIZE, 2 * self::ICON_SIZE);
+ if (!$res)
+ {
+ CLI::write('[talenticons] - image resource not created', CLI::LOG_ERROR);
+ return null;
+ }
+
+ for ($i = 0; $i < count($icons); $i++)
+ {
+ $imgFile = 'static/images/wow/icons/medium/'.strtolower($icons[$i]).'.jpg';
+ if (!file_exists($imgFile))
+ {
+ CLI::write('[talenticons] - raw image '.CLI::bold($imgFile). ' not found', CLI::LOG_ERROR);
+ return null;
+ }
+
+ $im = imagecreatefromjpeg($imgFile);
+
+ // colored
+ imagecopymerge($res, $im, $i * self::ICON_SIZE, 0, 0, 0, imageSX($im), imageSY($im), 100);
+
+ // grayscale
+ if (imageistruecolor($im))
+ imagetruecolortopalette($im, false, 256);
+
+ for ($j = 0; $j < imagecolorstotal($im); $j++)
+ {
+ $color = imagecolorsforindex($im, $j);
+ $gray = round(0.299 * $color['red'] + 0.587 * $color['green'] + 0.114 * $color['blue']);
+ imagecolorset($im, $j, $gray, $gray, $gray);
+ }
+ imagecopymerge($res, $im, $i * self::ICON_SIZE, self::ICON_SIZE, 0, 0, imageSX($im), imageSY($im), 100);
+ }
+
+ return $res;
+ }
+});
+
+?>
diff --git a/setup/tools/filegen/templates/global.js/0_user.js b/setup/tools/filegen/templates/global.js/0_user.js
new file mode 100644
index 00000000..cb968339
--- /dev/null
+++ b/setup/tools/filegen/templates/global.js/0_user.js
@@ -0,0 +1,88 @@
+// Needed for IE because it's dumb
+
+'abbr article aside audio canvas details figcaption figure footer header hgroup mark menu meter nav output progress section summary time video'.replace(/\w+/g,function(n){document.createElement(n)})
+
+
+// aowow - extend Date for holidaycal
+Date.prototype.getLocaleDay = function() {
+ const dayNo = this.getDay();
+ switch (Locale.getId())
+ {
+ case LOCALE_FRFR:
+ case LOCALE_DEDE:
+ case LOCALE_ESES:
+ case LOCALE_RURU:
+ return !dayNo ? 6 : dayNo - 1;
+ default:
+ return dayNo;
+ }
+};
+
+/*
+User-related functions
+TODO: Move global variables/functions into User class
+*/
+
+// IMPORTANT: If you update/change the permission groups below make sure to also update them in User.inc.php!
+
+/*********/
+/* ROLES */
+/*********/
+
+var U_GROUP_TESTER = 0x1;
+var U_GROUP_ADMIN = 0x2;
+var U_GROUP_EDITOR = 0x4;
+var U_GROUP_MOD = 0x8;
+var U_GROUP_BUREAU = 0x10;
+var U_GROUP_DEV = 0x20;
+var U_GROUP_VIP = 0x40;
+var U_GROUP_BLOGGER = 0x80;
+var U_GROUP_PREMIUM = 0x100;
+var U_GROUP_LOCALIZER = 0x200;
+var U_GROUP_SALESAGENT = 0x400;
+var U_GROUP_SCREENSHOT = 0x800;
+var U_GROUP_VIDEO = 0x1000;
+var U_GROUP_APIONLY = 0x2000;
+var U_GROUP_PENDING = 0x4000;
+
+
+/******************/
+/* ROLE SHORTCUTS */
+/******************/
+
+var U_GROUP_STAFF = U_GROUP_ADMIN | U_GROUP_EDITOR | U_GROUP_MOD | U_GROUP_BUREAU | U_GROUP_DEV | U_GROUP_BLOGGER | U_GROUP_LOCALIZER | U_GROUP_SALESAGENT;
+var U_GROUP_EMPLOYEE = U_GROUP_ADMIN | U_GROUP_BUREAU | U_GROUP_DEV;
+var U_GROUP_GREEN_TEXT = U_GROUP_MOD | U_GROUP_BUREAU | U_GROUP_DEV;
+var U_GROUP_PREMIUMISH = U_GROUP_PREMIUM | U_GROUP_EDITOR;
+var U_GROUP_MODERATOR = U_GROUP_ADMIN | U_GROUP_MOD | U_GROUP_BUREAU;
+var U_GROUP_COMMENTS_MODERATOR = U_GROUP_BUREAU | U_GROUP_MODERATOR | U_GROUP_LOCALIZER;
+var U_GROUP_PREMIUM_PERMISSIONS = U_GROUP_PREMIUM | U_GROUP_STAFF | U_GROUP_VIP;
+
+var g_users = {};
+var g_favorites = [];
+var g_customColors = {};
+
+function g_isUsernameValid(username) {
+ return (username.match(/[^a-z0-9]/i) == null && username.length >= 4 && username.length <= 16);
+}
+
+var User = new function() {
+ var self = this;
+
+ /**********/
+ /* PUBLIC */
+ /**********/
+
+ self.hasPermissions = function(roles)
+ {
+ if(!roles)
+ return true;
+
+ return !!(g_user.roles & roles);
+ }
+
+ /**********/
+ /* PRIVATE */
+ /**********/
+
+};
diff --git a/setup/tools/filegen/templates/global.js/ajax.js b/setup/tools/filegen/templates/global.js/ajax.js
new file mode 100644
index 00000000..22761baa
--- /dev/null
+++ b/setup/tools/filegen/templates/global.js/ajax.js
@@ -0,0 +1,51 @@
+function Ajax(url, opt)
+{
+ if (!url)
+ return;
+
+ var _;
+
+ try { _ = new XMLHttpRequest() } catch (e)
+ {
+ try { _ = new ActiveXObject("Msxml2.XMLHTTP") } catch (e)
+ {
+ try { _ = new ActiveXObject("Microsoft.XMLHTTP") } catch (e)
+ {
+ if (window.createRequest)
+ _ = window.createRequest();
+ else
+ {
+ alert(LANG.message_ajaxnotsupported);
+ return;
+ }
+ }
+ }
+ }
+
+ this.request = _;
+
+ $WH.cO(this, opt);
+ this.method = this.method || (this.params && 'POST') || 'GET';
+
+ _.open(this.method, url, this.async == null ? true : this.async);
+ _.onreadystatechange = Ajax.onReadyStateChange.bind(this);
+
+ if (this.method.toUpperCase() == 'POST')
+ _.setRequestHeader('Content-Type', (this.contentType || 'application/x-www-form-urlencoded') + '; charset=' + (this.encoding || 'UTF-8'));
+
+ _.send(this.params);
+}
+
+Ajax.onReadyStateChange = function()
+{
+ if (this.request.readyState == 4)
+ {
+ if (this.request.status == 0 || (this.request.status >= 200 && this.request.status < 300))
+ this.onSuccess != null && this.onSuccess(this.request, this);
+ else
+ this.onFailure != null && this.onFailure(this.request, this);
+
+ if (this.onComplete != null)
+ this.onComplete(this.request, this);
+ }
+};
diff --git a/setup/tools/filegen/templates/global.js/animations.js b/setup/tools/filegen/templates/global.js/animations.js
new file mode 100644
index 00000000..da37f6fe
--- /dev/null
+++ b/setup/tools/filegen/templates/global.js/animations.js
@@ -0,0 +1,123 @@
+/*
+ * jQuery Color Animations
+ * Copyright 2007 John Resig
+ * Released under the MIT and GPL licenses.
+ */
+
+(function(jQuery){
+
+ // We override the animation for all of these color styles
+ jQuery.each(['backgroundColor', 'borderBottomColor', 'borderLeftColor', 'borderRightColor', 'borderTopColor', 'color', 'outlineColor'], function(i,attr){
+ jQuery.fx.step[attr] = function(fx){
+ if ( fx.state == 0 ) {
+ fx.start = getColor( fx.elem, attr );
+ fx.end = getRGB( fx.end );
+ }
+
+ fx.elem.style[attr] = "rgb(" + [
+ Math.max(Math.min( parseInt((fx.pos * (fx.end[0] - fx.start[0])) + fx.start[0]), 255), 0),
+ Math.max(Math.min( parseInt((fx.pos * (fx.end[1] - fx.start[1])) + fx.start[1]), 255), 0),
+ Math.max(Math.min( parseInt((fx.pos * (fx.end[2] - fx.start[2])) + fx.start[2]), 255), 0)
+ ].join(",") + ")";
+ }
+ });
+
+ // Color Conversion functions from highlightFade
+ // By Blair Mitchelmore
+ // http://jquery.offput.ca/highlightFade/
+
+ // Parse strings looking for color tuples [255,255,255]
+ function getRGB(color) {
+ var result;
+
+ // Check if we're already dealing with an array of colors
+ if ( color && color.constructor == Array && color.length == 3 )
+ return color;
+
+ // Look for rgb(num,num,num)
+ if (result = /rgb\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*\)/.exec(color))
+ return [parseInt(result[1]), parseInt(result[2]), parseInt(result[3])];
+
+ // Look for rgb(num%,num%,num%)
+ if (result = /rgb\(\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*\)/.exec(color))
+ return [parseFloat(result[1])*2.55, parseFloat(result[2])*2.55, parseFloat(result[3])*2.55];
+
+ // Look for #a0b1c2
+ if (result = /#([a-fA-F0-9]{2})([a-fA-F0-9]{2})([a-fA-F0-9]{2})/.exec(color))
+ return [parseInt(result[1],16), parseInt(result[2],16), parseInt(result[3],16)];
+
+ // Look for #fff
+ if (result = /#([a-fA-F0-9])([a-fA-F0-9])([a-fA-F0-9])/.exec(color))
+ return [parseInt(result[1]+result[1],16), parseInt(result[2]+result[2],16), parseInt(result[3]+result[3],16)];
+
+ // Otherwise, we're most likely dealing with a named color
+ return colors[jQuery.trim(color).toLowerCase()];
+ }
+
+ function getColor(elem, attr) {
+ var color;
+
+ do {
+ color = jQuery.curCSS(elem, attr);
+
+ // Keep going until we find an element that has color, or we hit the body
+ if ( color != '' && color != 'transparent' || jQuery.nodeName(elem, "body") )
+ break;
+
+ attr = "backgroundColor";
+ } while ( elem = elem.parentNode );
+
+ return getRGB(color);
+ }
+
+ // Some named colors to work with
+ // From Interface by Stefan Petre
+ // http://interface.eyecon.ro/
+
+ var colors = {
+ aqua:[0,255,255],
+ azure:[240,255,255],
+ beige:[245,245,220],
+ black:[0,0,0],
+ blue:[0,0,255],
+ brown:[165,42,42],
+ cyan:[0,255,255],
+ darkblue:[0,0,139],
+ darkcyan:[0,139,139],
+ darkgrey:[169,169,169],
+ darkgreen:[0,100,0],
+ darkkhaki:[189,183,107],
+ darkmagenta:[139,0,139],
+ darkolivegreen:[85,107,47],
+ darkorange:[255,140,0],
+ darkorchid:[153,50,204],
+ darkred:[139,0,0],
+ darksalmon:[233,150,122],
+ darkviolet:[148,0,211],
+ fuchsia:[255,0,255],
+ gold:[255,215,0],
+ green:[0,128,0],
+ indigo:[75,0,130],
+ khaki:[240,230,140],
+ lightblue:[173,216,230],
+ lightcyan:[224,255,255],
+ lightgreen:[144,238,144],
+ lightgrey:[211,211,211],
+ lightpink:[255,182,193],
+ lightyellow:[255,255,224],
+ lime:[0,255,0],
+ magenta:[255,0,255],
+ maroon:[128,0,0],
+ navy:[0,0,128],
+ olive:[128,128,0],
+ orange:[255,165,0],
+ pink:[255,192,203],
+ purple:[128,0,128],
+ violet:[128,0,128],
+ red:[255,0,0],
+ silver:[192,192,192],
+ white:[255,255,255],
+ yellow:[255,255,0]
+ };
+
+})(jQuery);
diff --git a/setup/tools/filegen/templates/global.js/announcement.js b/setup/tools/filegen/templates/global.js/announcement.js
new file mode 100644
index 00000000..82628760
--- /dev/null
+++ b/setup/tools/filegen/templates/global.js/announcement.js
@@ -0,0 +1,177 @@
+var Announcement = function(opt)
+{
+ if (!opt)
+ opt = {};
+
+ $WH.cO(this, opt);
+
+ if (this.parent)
+ this.parentDiv = $WH.ge(this.parent);
+ else
+ return;
+
+ if (g_user.id > 0 && (!g_cookiesEnabled() || g_getWowheadCookie('announcement-' + this.id) == 'closed'))
+ return;
+
+ this.initialize();
+};
+
+Announcement.prototype = {
+ initialize: function()
+ {
+ // aowow - animation fix
+ // this.parentDiv.style.display = 'none';
+ this.parentDiv.style.opacity = '0';
+
+ if (this.mode === undefined || this.mode == 1)
+ this.parentDiv.className = 'announcement announcement-contenttop';
+ else
+ this.parentDiv.className = 'announcement announcement-pagetop';
+
+ var div = this.innerDiv = $WH.ce('div');
+ div.className = 'announcement-inner text';
+ this.setStyle(this.style);
+
+ var a = null;
+ var id = parseInt(this.id);
+
+ if (g_user && (g_user.roles & (U_GROUP_ADMIN|U_GROUP_BUREAU)) > 0 && Math.abs(id) > 0)
+ {
+ if (id < 0)
+ {
+ a = $WH.ce('a');
+ a.style.cssFloat = a.style.styleFloat = 'right';
+ a.href = '?admin=announcements&id=' + Math.abs(id) + '&status=2';
+ a.onclick = function() { return confirm('Are you sure you want to delete ' + this.name + '?'); };
+ $WH.ae(a, $WH.ct('Delete'));
+ var small = $WH.ce('small');
+ $WH.ae(small, a);
+ $WH.ae(div, small);
+
+ a = $WH.ce('a');
+ a.style.cssFloat = a.style.styleFloat = 'right';
+ a.style.marginRight = '10px';
+ a.href = '?admin=announcements&id=' + Math.abs(id) + '&status=' + (this.status == 1 ? 0 : 1);
+ a.onclick = function() { return confirm('Are you sure you want to delete ' + this.name + '?'); };
+ $WH.ae(a, $WH.ct((this.status == 1 ? 'Disable' : 'Enable')));
+ var small = $WH.ce('small');
+ $WH.ae(small, a);
+ $WH.ae(div, small);
+ }
+
+ a = $WH.ce('a');
+ a.style.cssFloat = a.style.styleFloat = 'right';
+ a.style.marginRight = '22px';
+ a.href = '?admin=announcements&id=' + Math.abs(id) + '&edit';
+ $WH.ae(a, $WH.ct('Edit announcement'));
+ var small = $WH.ce('small');
+ $WH.ae(small, a);
+ $WH.ae(div, small);
+ }
+
+ var markupDiv = $WH.ce('div');
+ markupDiv.id = this.parent + '-markup';
+ $WH.ae(div, markupDiv);
+
+ if (id >= 0)
+ {
+ a = $WH.ce('a');
+
+ a.id = 'closeannouncement';
+ a.href = 'javascript:;';
+ a.className = 'announcement-close';
+ if (this.nocookie)
+ a.onclick = this.hide.bind(this);
+ else
+ a.onclick = this.markRead.bind(this);
+
+ $WH.ae(div, a);
+ g_addTooltip(a, LANG.close);
+ }
+
+ $WH.ae(div, $WH.ce('div', { style: { clear: 'both' } }));
+
+ $WH.ae(this.parentDiv, div);
+
+ this.setText(this.text);
+
+ setTimeout(this.show.bind(this), 500); // Delay to avoid visual lag
+ },
+
+ show: function()
+ {
+ // $(this.parentDiv).animate({
+ // opacity: 'show',
+ // height: 'show'
+ // },{
+ // duration: 333
+ // });
+
+ // aowow - animation fix - jQuery.animate hard snaps into place after half the time passed
+ this.parentDiv.style.opacity = '100';
+ this.parentDiv.style.height = (this.parentDiv.offsetHeight + 10) + 'px';
+
+ $WH.Track.nonInteractiveEvent({
+ category: 'Announcements',
+ action: 'Show',
+ label: '' + this.name
+ });
+ },
+
+ hide: function()
+ {
+ // $(this.parentDiv).animate({
+ // opacity: 'hide',
+ // height: 'hide'
+ // },{
+ // duration: 200
+ // });
+
+ // aowow - animation fix - jQuery.animate hard snaps into place after half the time passed
+ this.parentDiv.style.opacity = '0';
+ this.parentDiv.style.height = '0px';
+ setTimeout(function() {
+ this.parentDiv.style.display = 'none';
+ }.bind(this), 400);
+ },
+
+ markRead: function()
+ {
+ $WH.Track.interactiveEvent({
+ category: 'Announcements',
+ action: 'Close',
+ label: '' + this.name
+ });
+ g_setWowheadCookie('announcement-' + this.id, 'closed');
+ this.hide();
+ },
+
+ setStyle: function(style)
+ {
+ this.style = style;
+ this.innerDiv.setAttribute('style', style);
+ },
+
+ setText: function(text)
+ {
+ this.text = text;
+ Markup.printHtml(this.text, this.parent + '-markup');
+
+ let parent = $WH.ge(this.parent + '-markup');
+ $WH.qsa('a', parent).forEach(link => {
+ $WH.aE(link, 'click', () => {
+ let label = 'unknown';
+ let txt = g_getFirstTextContent(link);
+ if (txt)
+ label = g_urlize(txt).substr(0, 80);
+ else if (link.title)
+ label = g_urlize(link.title).substr(0, 80);
+ else if (link.id)
+ label = g_urlize(link.id).substr(0, 80);
+
+ label = `${ this.id || 0 }-${ label }`;
+ $WH.Track.linkClick(link, { category: 'Announcements', label: label });
+ });
+ });
+ }
+};
diff --git a/setup/tools/filegen/templates/global.js/audio.js b/setup/tools/filegen/templates/global.js/audio.js
new file mode 100644
index 00000000..106a93c4
--- /dev/null
+++ b/setup/tools/filegen/templates/global.js/audio.js
@@ -0,0 +1,377 @@
+var g_audiocontrols = {
+ __windowloaded: false,
+};
+var g_audioplaylist = {};
+
+// aowow - why is window.JSON here, wedged between the audio controls. It's only used for SearchBrowseButtons (and sourced by Listview)
+if (!window.JSON) {
+ window.JSON = {
+ parse: function (sJSON) {
+ return eval("(" + sJSON + ")");
+ },
+
+ stringify: function (obj) {
+ if (obj instanceof Object)
+ {
+ var str = '';
+ if (obj.constructor === Array)
+ {
+ for (var i = 0; i < obj.length; str += this.stringify(obj[i]) + ',', i++) {}
+ return '[' + str.substr(0, str.length - 1) + ']';
+ }
+ if (obj.toString !== Object.prototype.toString)
+ return '"' + obj.toString().replace(/"/g, '\\$&') + '"';
+
+ for (var e in obj)
+ str += '"' + e.replace(/"/g, '\\$&') + '":' + this.stringify(obj[e]) + ',';
+
+ return '{' + str.substr(0, str.length - 1) + '}';
+ }
+
+ return typeof obj === 'string' ? '"' + obj.replace(/"/g, '\\$&') + '"' : String(obj);
+ }
+ }
+}
+
+AudioControls = function ()
+{
+ var fileIdx = -1;
+ var canPlay = false;
+ var looping = false;
+ var fullPlayer = false;
+ var autoStart = false;
+ var controls = {};
+ var playlist = [];
+ var url = '';
+
+ function updatePlayer(_self, itr, doPlay)
+ {
+ var elAudio = $WH.ce('audio');
+ elAudio.preload = 'none';
+ elAudio.controls = 'true';
+ $(elAudio).click(function (s) { s.stopPropagation() });
+ elAudio.style.marginTop = '5px';
+
+ controls.audio.parentNode.replaceChild(elAudio, controls.audio);
+ controls.audio = elAudio;
+ $WH.aE(controls.audio, 'ended', setNextTrack.bind(_self));
+
+ if (doPlay)
+ {
+ elAudio.preload = 'auto';
+ autoStart = true;
+ $WH.aE(controls.audio, 'canplaythrough', autoplay.bind(this));
+ }
+
+ if (!canPlay)
+ controls.table.style.visibility = 'visible';
+
+ var file;
+ do
+ {
+ fileIdx += itr;
+ if (fileIdx > playlist.length - 1)
+ {
+ fileIdx = 0;
+ if (!canPlay)
+ {
+ var div = $WH.ce('div');
+ // div.className = 'minibox'; Aowow custom
+ div.className = 'minibox minibox-left';
+ $WH.st(div, $WH.sprintf(LANG.message_browsernoaudio, file.type));
+ controls.table.parentNode.replaceChild(div, controls.table);
+ return
+ }
+ }
+
+ if (fileIdx < 0)
+ fileIdx = playlist.length - 1;
+
+ file = playlist[fileIdx];
+ }
+ while (controls.audio.canPlayType(file.type) == '');
+
+ var elSource = $WH.ce('source');
+ elSource.src = file.url;
+ elSource.type = file.type;
+ $WH.ae(controls.audio, elSource);
+
+ if (controls.hasOwnProperty('title'))
+ {
+ if (url)
+ {
+ $WH.ee(controls.title);
+ var a = $WH.ce('a');
+ a.href = url;
+ $WH.st(a, '"' + file.title + '"');
+ $WH.ae(controls.title, a);
+ }
+ else
+ $WH.st(controls.title, '"' + file.title + '"');
+ }
+
+ if (controls.hasOwnProperty('trackdisplay'))
+ $WH.st(controls.trackdisplay, '' + (fileIdx + 1) + ' / ' + playlist.length);
+
+ if (!canPlay)
+ {
+ canPlay = true;
+ for (var i = fileIdx + 1; i <= playlist.length - 1; i++)
+ {
+ if (controls.audio.canPlayType(playlist[i].type))
+ {
+ $(controls.controlsdiv).children('a').removeClass('button-red-disabled');
+ break;
+ }
+ }
+ }
+
+ if (controls.hasOwnProperty('addbutton'))
+ {
+ $(controls.addbutton).removeClass('button-red-disabled');
+ // $WH.st(controls.addbutton, LANG.add); Aowow: doesnt work with RedButtons
+ RedButton.setText(controls.addbutton, LANG.add);
+ }
+ }
+
+ function autoplay()
+ {
+ if (!autoStart)
+ return;
+
+ autoStart = false;
+ controls.audio.play();
+ }
+
+ this.init = function (files, parent, opt)
+ {
+ if (!$WH.is_array(files))
+ return;
+
+ if (files.length == 0)
+ return;
+
+ if ((parent.id == '') || g_audiocontrols.hasOwnProperty(parent.id))
+ {
+ var i = 0;
+ while (g_audiocontrols.hasOwnProperty('auto-audiocontrols-' + (++i))) {}
+ parent.id = 'auto-audiocontrols-' + i;
+ }
+
+ g_audiocontrols[parent.id] = this;
+
+ if (typeof opt == 'undefined')
+ opt = {};
+
+ looping = !!opt.loop;
+ if (opt.hasOwnProperty('url'))
+ url = opt.url;
+
+ playlist = files;
+ controls.div = parent;
+
+ if (!opt.listview)
+ {
+ var tbl = $WH.ce('table', { className: 'audio-controls' });
+ controls.table = tbl;
+ controls.table.style.visibility = 'hidden';
+ $WH.ae(controls.div, tbl);
+
+ var tr = $WH.ce('tr');
+ $WH.ae(tbl, tr);
+
+ var td = $WH.ce('td');
+ $WH.ae(tr, td);
+
+ controls.audio = $WH.ce('div');
+ $WH.ae(td, controls.audio);
+
+ controls.title = $WH.ce('div', { className: 'audio-controls-title' });
+ $WH.ae(td, controls.title);
+
+ controls.controlsdiv = $WH.ce('div', { className: 'audio-controls-pagination' });
+ $WH.ae(td, controls.controlsdiv);
+
+ var prevBtn = createButton(LANG.previous, true);
+ $WH.ae(controls.controlsdiv, prevBtn);
+ $WH.aE(prevBtn, 'click', this.btnPrevTrack.bind(this));
+
+ controls.trackdisplay = $WH.ce('div', { className: 'audio-controls-pagination-track' });
+ $WH.ae(controls.controlsdiv, controls.trackdisplay);
+
+ var nextBtn = createButton(LANG.next, true);
+ $WH.ae(controls.controlsdiv, nextBtn);
+ $WH.aE(nextBtn, 'click', this.btnNextTrack.bind(this))
+ }
+ else
+ {
+ fullPlayer = true;
+ var div = $WH.ce('div');
+ controls.table = div;
+ $WH.ae(controls.div, div);
+
+ controls.audio = $WH.ce('div');
+ $WH.ae(div, controls.audio);
+
+ controls.trackdisplay = opt.trackdisplay;
+ controls.controlsdiv = $WH.ce('span');
+ $WH.ae(div, controls.controlsdiv);
+ }
+
+ if (g_audioplaylist.isEnabled() && !opt.fromplaylist)
+ {
+ var addBtn = createButton(LANG.add);
+ $WH.ae(controls.controlsdiv, addBtn);
+ $WH.aE(addBtn, 'click', this.btnAddToPlaylist.bind(this, addBtn));
+ controls.addbutton = addBtn;
+
+ if (fullPlayer)
+ addBtn.style.verticalAlign = '50%';
+ }
+
+ if (g_audiocontrols.__windowloaded)
+ this.btnNextTrack();
+ };
+
+ function setNextTrack()
+ {
+ updatePlayer(this, 1, (looping || (fileIdx < (playlist.length - 1))));
+ }
+
+ this.btnNextTrack = function ()
+ {
+ updatePlayer(this, 1, (canPlay && (controls.audio.readyState > 1) && (!controls.audio.paused)));
+ };
+
+ this.btnPrevTrack = function ()
+ {
+ updatePlayer(this, -1, (canPlay && (controls.audio.readyState > 1) && (!controls.audio.paused)));
+ };
+
+ this.btnAddToPlaylist = function (_self)
+ {
+ if (fullPlayer)
+ {
+ for (var i = 0; i < playlist.length; i++)
+ g_audioplaylist.addSound(playlist[i]);
+ }
+ else
+ g_audioplaylist.addSound(playlist[fileIdx]);
+
+ _self.className += ' button-red-disabled';
+ // $WH.st(_self, LANG.added); // Aowow doesn't work with RedButtons
+ RedButton.setText(_self, LANG.added);
+ };
+
+ this.isPlaying = function ()
+ {
+ return !controls.audio.paused;
+ };
+
+ this.removeSelf = function ()
+ {
+ controls.table.parentNode.removeChild(controls.table);
+ delete g_audiocontrols[controls.div];
+ };
+
+ function createButton(text, disabled)
+ {
+ return $WH.g_createButton(text, null, {
+ disabled: disabled,
+ // 'float': false, Aowow - adapted style
+ // style: 'margin:0 12px; display:inline-block'
+ style: 'margin:0 12px; display:inline-block; float:inherit; '
+ });
+ }
+};
+
+$WH.aE(window, 'load', function ()
+{
+ g_audiocontrols.__windowloaded = true;
+ for (var i in g_audiocontrols)
+ if (i.substr(0, 2) != '__')
+ g_audiocontrols[i].btnNextTrack();
+});
+
+AudioPlaylist = function ()
+{
+ var enabled = false;
+ var playlist = [];
+ var player, container;
+
+ this.init = function ()
+ {
+ if (!$WH.localStorage.isSupported())
+ return;
+
+ enabled = true;
+
+ var tracks;
+ if (tracks = $WH.localStorage.get('AudioPlaylist'))
+ playlist = JSON.parse(tracks);
+ };
+
+ this.savePlaylist = function ()
+ {
+ if (!enabled)
+ return false;
+
+ $WH.localStorage.set('AudioPlaylist', JSON.stringify(playlist));
+ };
+
+ this.isEnabled = function ()
+ {
+ return enabled;
+ };
+
+ this.addSound = function (track)
+ {
+ if (!enabled)
+ return false;
+
+ this.init();
+ playlist.push(track);
+ this.savePlaylist();
+ };
+
+ this.deleteSound = function (idx)
+ {
+ if (idx < 0)
+ playlist = [];
+ else
+ playlist.splice(idx, 1);
+
+ this.savePlaylist();
+
+ if (!player.isPlaying())
+ {
+ player.removeSelf();
+ this.setAudioControls(container);
+ }
+
+ if (playlist.length == 0)
+ $WH.Tooltip.hide();
+ };
+
+ this.getList = function ()
+ {
+ var buf = [];
+ for (var i = 0; i < playlist.length; i++)
+ buf.push(playlist[i].title);
+
+ return buf;
+ };
+
+ this.setAudioControls = function (parent)
+ {
+ if (!enabled)
+ return false;
+
+ container = parent;
+ player = new AudioControls();
+ player.init(playlist, container, { loop: true, fromplaylist: true });
+ };
+};
+
+g_audioplaylist = (new AudioPlaylist);
+g_audioplaylist.init();
diff --git a/setup/tools/filegen/templates/global.js/clicktocopy.js b/setup/tools/filegen/templates/global.js/clicktocopy.js
new file mode 100644
index 00000000..d8c20bfd
--- /dev/null
+++ b/setup/tools/filegen/templates/global.js/clicktocopy.js
@@ -0,0 +1,122 @@
+$WH.clickToCopy = function (el, textOrFn, opt)
+{
+ opt = opt || {};
+
+ $WH.aE(el, 'click', $WH.clickToCopy.copy.bind(null, el, textOrFn, opt));
+ // $WH.preventSelectStart(el);
+
+ el.classList.add('click-to-copy');
+
+ if (opt.modifyTooltip)
+ {
+ el._fixTooltip = function (e) {
+ return e + '
' + $WH.ce('span', { className: 'q2', innerHTML: $WH.clickToCopy.getTooltip(false, opt) }).outerHTML;
+ };
+
+ opt.overrideOtherTooltips = false;
+ }
+
+ // aowow - fitted to old system
+ // $WH.Tooltips.attach(
+ $WH.Tooltip.simple(
+ el,
+ $WH.clickToCopy.getTooltip.bind(null, false, opt),
+ undefined,
+ // {
+ /* byCursor: ! */ opt.attachToElement,
+ // stopPropagation: opt.overrideOtherTooltips
+ // }
+ );
+};
+
+$WH.clickToCopy.copy = function (el, textOrFn, opt, ev)
+{
+ ev.preventDefault();
+ ev.stopPropagation();
+
+ if (textOrFn === undefined)
+ {
+ if (!el.childNodes[0] || !el.childNodes[0].textContent)
+ {
+ let text = 'Could not find text to copy.';
+ // $WH.error(text, el);
+
+ if (opt.attachToElement)
+ $WH.Tooltip.show(el, text, 'q10');
+ else
+ $WH.Tooltip.showAtCursor(ev, text, 'q10');
+
+ return;
+ }
+
+ textOrFn = el.childNodes[0].textContent;
+ }
+ else if (typeof textOrFn === 'function')
+ textOrFn = textOrFn();
+
+ $WH.copyToClipboard(textOrFn);
+
+ if (opt.attachToElement)
+ $WH.Tooltip.show(el, $WH.clickToCopy.getTooltip(true, opt));
+ else
+ $WH.Tooltip.showAtCursor(ev, $WH.clickToCopy.getTooltip(true, opt));
+};
+
+$WH.clickToCopy.getTooltip = function (clicked, opt)
+{
+ let txt = '';
+ let attr = undefined;
+
+ if (clicked)
+ {
+ txt = ' ' + LANG.copied;
+ attr = { className: 'q1 icon-tick' };
+ }
+ else
+ txt = LANG.clickToCopy;
+
+ let tt = $WH.ce('div', attr, $WH.ct(txt));
+
+ if (opt.prefix)
+ {
+ tt.style.marginTop = '10px';
+ let prefix = typeof opt.prefix === 'function' ? opt.prefix() : opt.prefix;
+ return prefix + tt.outerHTML;
+ }
+
+ return tt.outerHTML;
+};
+
+$WH.copyToClipboard = function (text, t)
+{
+ if (!$WH.copyToClipboard.hiddenInput)
+ {
+ $WH.copyToClipboard.hiddenInput = $WH.ce('textarea', { className: 'hidden-element' });
+ $WH.ae(document.body, $WH.copyToClipboard.hiddenInput);
+ }
+
+ $WH.copyToClipboard.hiddenInput.value = text;
+
+ let isEmpty = $WH.copyToClipboard.hiddenInput.value === '';
+ if (isEmpty)
+ $WH.copyToClipboard.hiddenInput.value = LANG.nothingToCopy_tip;
+
+ $WH.copyToClipboard.hiddenInput.focus();
+ $WH.copyToClipboard.hiddenInput.select();
+
+ if (!document.execCommand('copy'))
+ prompt(null, text);
+
+ $WH.copyToClipboard.hiddenInput.blur();
+
+ if (t)
+ {
+ if (isEmpty)
+ $WH.Tooltips.showFadingTooltipAtCursor(LANG.nothingToCopy_tip, t, 'q10');
+ else
+ {
+ let e = $WH.ce('span', { className: 'q1 icon-tick' }, $WH.ct(' ' + LANG.copied));
+ $WH.Tooltips.showFadingTooltipAtCursor(e.outerHTML, t);
+ }
+ }
+};
diff --git a/setup/tools/filegen/templates/global.js/comments.js b/setup/tools/filegen/templates/global.js/comments.js
new file mode 100644
index 00000000..352a6be2
--- /dev/null
+++ b/setup/tools/filegen/templates/global.js/comments.js
@@ -0,0 +1,459 @@
+/* Note: comment replies are called "comments" because part of this code was taken from another project of mine. */
+
+function SetupReplies(post, comment)
+{
+ SetupAddEditComment(post, comment, false);
+ SetupShowMoreComments(post, comment);
+
+ post.find('.comment-reply-row').each(function () { SetupRepliesControls($(this), comment); });
+ post.find('.comment-reply-row').hover(function () { $(this).find('span').attr('data-hover', 'true'); }, function () { $(this).find('span').attr('data-hover', 'false'); });
+}
+
+function SetupAddEditComment(post, comment, edit)
+{
+ /* Variables that will be set by Initialize() */
+ var Form = null;
+ var Body = null;
+ var AddButton = null;
+ var TextCounter = null;
+ var AjaxLoader = null;
+ var FormContainer = null;
+ var DialogTableRowContainer = null;
+
+ /* Constants */
+ var MIN_LENGTH = 15;
+ var MAX_LENGTH = 600;
+
+ /* State keeping booleans */
+ var Initialized = false;
+ var Active = false;
+ var Flashing = false;
+ var Submitting = false;
+
+ /* Shortcuts */
+ var CommentsTable = post.find('.comment-replies > table');
+ var AddCommentLink = post.find('.add-reply');
+ var CommentsCount = comment.replies.length;
+
+ if(edit)
+ Open();
+ else
+ AddCommentLink.click(function () { Open(); });
+
+ function Initialize()
+ {
+ if (Initialized)
+ return;
+
+ Initialized = true;
+
+ var row = $('
');
+
+ if(edit)
+ row.addClass('comment-reply-row').addClass('reply-edit-row');
+
+ row.html(' | ' +
+ '