diff --git a/includes/defines.php b/includes/defines.php
index 72222380..747140a1 100644
--- a/includes/defines.php
+++ b/includes/defines.php
@@ -323,6 +323,7 @@ define('PROFILER_CU_PROFILE', 0x08);
define('PROFILER_CU_NEEDS_RESYNC', 0x10);
define('MAX_LEVEL', 80);
+define('MAX_SKILL', 450);
define('WOW_BUILD', 12340);
// Loot handles
@@ -1117,6 +1118,22 @@ define('SPELL_ATTR7_UNK29', 0x20000000); // Unknown a
define('SPELL_ATTR7_UNK30', 0x40000000); // Unknown attribute 30@Attr7
define('SPELL_ATTR7_CLIENT_INDICATOR', 0x80000000); // Client indicator (client only)
+
+// (some) Skill ids
+define('SKILL_BLACKSMITHING', 164);
+define('SKILL_LEATHERWORKING', 165);
+define('SKILL_ALCHEMY', 171);
+define('SKILL_HERBALISM', 182);
+define('SKILL_MINING', 186);
+define('SKILL_TAILORING', 197);
+define('SKILL_ENGINEERING', 202);
+define('SKILL_ENCHANTING', 333);
+define('SKILL_SKINNING', 393);
+define('SKILL_JEWELCRAFTING', 755);
+define('SKILL_INSCRIPTION', 773);
+define('SKILL_LOCKPICKING', 633);
+
+
// AchievementCriteriaCondition
define('ACHIEVEMENT_CRITERIA_CONDITION_NO_DEATH', 1); // reset progress on death
define('ACHIEVEMENT_CRITERIA_CONDITION_BG_MAP', 3); // requires you to be on specific map, reset at change
diff --git a/includes/game.php b/includes/game.php
index 43999dab..82b4f6cf 100644
--- a/includes/game.php
+++ b/includes/game.php
@@ -371,6 +371,32 @@ class Game
return [$quotes, $nQuotes, $soundIds];
}
+ public static function getBreakpointsForSkill(int $skillId, int $reqLevel) : array
+ {
+ switch ($skillId)
+ {
+ case SKILL_HERBALISM:
+ case SKILL_LOCKPICKING:
+ case SKILL_JEWELCRAFTING:
+ case SKILL_INSCRIPTION:
+ case SKILL_SKINNING:
+ case SKILL_MINING:
+ $points = [$reqLevel]; // red/orange
+
+ if ($reqLevel + 25 <= MAX_SKILL) // orange/yellow
+ $points[] = $reqLevel + 25;
+
+ if ($reqLevel + 50 <= MAX_SKILL) // yellow/green
+ $points[] = $reqLevel + 50;
+
+ if ($reqLevel + 100 <= MAX_SKILL) // green/grey
+ $points[] = $reqLevel + 100;
+
+ return $points;
+ default:
+ return [$reqLevel];
+ }
+ }
}
?>
diff --git a/includes/types/creature.class.php b/includes/types/creature.class.php
index c76a8d85..45d3882b 100644
--- a/includes/types/creature.class.php
+++ b/includes/types/creature.class.php
@@ -306,7 +306,7 @@ class CreatureListFilter extends Filter
11 => [FILTER_CR_BOOLEAN, 'pickpocketLootId', ], // pickpocketable
12 => [FILTER_CR_CALLBACK, 'cbMoneyDrop', null, null ], // averagemoneydropped [op] [int]
15 => [FILTER_CR_CALLBACK, 'cbSpecialSkinLoot', NPC_TYPEFLAG_HERBLOOT, null ], // gatherable [yn]
- 16 => [FILTER_CR_CALLBACK, 'cbSpecialSkinLoot', NPC_TYPEFLAG_ENGINEERLOOT, null ], // minable [yn]
+ 16 => [FILTER_CR_CALLBACK, 'cbSpecialSkinLoot', NPC_TYPEFLAG_MININGLOOT, null ], // minable [yn]
18 => [FILTER_CR_FLAG, 'npcflag', NPC_FLAG_AUCTIONEER ], // auctioneer
19 => [FILTER_CR_FLAG, 'npcflag', NPC_FLAG_BANKER ], // banker
20 => [FILTER_CR_FLAG, 'npcflag', NPC_FLAG_BATTLEMASTER ], // battlemaster
@@ -329,7 +329,7 @@ class CreatureListFilter extends Filter
41 => [FILTER_CR_NYI_PH, 1, null ], // haslocation [yn] [staff]
42 => [FILTER_CR_CALLBACK, 'cbReputation', '>', null ], // increasesrepwith [enum]
43 => [FILTER_CR_CALLBACK, 'cbReputation', '<', null ], // decreasesrepwith [enum]
- 44 => [FILTER_CR_CALLBACK, 'cbSpecialSkinLoot', NPC_TYPEFLAG_MININGLOOT, null ] // salvageable [yn]
+ 44 => [FILTER_CR_CALLBACK, 'cbSpecialSkinLoot', NPC_TYPEFLAG_ENGINEERLOOT, null ] // salvageable [yn]
);
// fieldId => [checkType, checkValue[, fieldIsArray]]
diff --git a/includes/types/profile.class.php b/includes/types/profile.class.php
index 4069f0be..72f39728 100644
--- a/includes/types/profile.class.php
+++ b/includes/types/profile.class.php
@@ -250,36 +250,36 @@ class ProfileListFilter extends Filter
);
protected $genericFilter = array( // misc (bool): _NUMERIC => useFloat; _STRING => localized; _FLAG => match Value; _BOOLEAN => stringSet
- 2 => [FILTER_CR_NUMERIC, 'gearscore', NUM_CAST_INT ], // gearscore [num]
- 3 => [FILTER_CR_CALLBACK, 'cbAchievs', null, null], // achievementpoints [num]
- 5 => [FILTER_CR_NUMERIC, 'talenttree1', NUM_CAST_INT ], // talenttree1 [num]
- 6 => [FILTER_CR_NUMERIC, 'talenttree2', NUM_CAST_INT ], // talenttree2 [num]
- 7 => [FILTER_CR_NUMERIC, 'talenttree3', NUM_CAST_INT ], // talenttree3 [num]
- 9 => [FILTER_CR_STRING, 'g.name', ], // guildname
- 10 => [FILTER_CR_CALLBACK, 'cbHasGuildRank', null, null], // guildrank
- 12 => [FILTER_CR_CALLBACK, 'cbTeamName', null, null], // teamname2v2
- 15 => [FILTER_CR_CALLBACK, 'cbTeamName', null, null], // teamname3v3
- 18 => [FILTER_CR_CALLBACK, 'cbTeamName', null, null], // teamname5v5
- 13 => [FILTER_CR_CALLBACK, 'cbTeamRating', null, null], // teamrtng2v2
- 16 => [FILTER_CR_CALLBACK, 'cbTeamRating', null, null], // teamrtng3v3
- 19 => [FILTER_CR_CALLBACK, 'cbTeamRating', null, null], // teamrtng5v5
- 14 => [FILTER_CR_NYI_PH, 0 ], // teamcontrib2v2 [num]
- 17 => [FILTER_CR_NYI_PH, 0 ], // teamcontrib3v3 [num]
- 20 => [FILTER_CR_NYI_PH, 0 ], // teamcontrib5v5 [num]
- 21 => [FILTER_CR_CALLBACK, 'cbWearsItems', null, null], // wearingitem [str]
- 23 => [FILTER_CR_CALLBACK, 'cbCompletedAcv', null, null], // completedachievement
- 25 => [FILTER_CR_CALLBACK, 'cbProfession', 171, null], // alchemy [num]
- 26 => [FILTER_CR_CALLBACK, 'cbProfession', 164, null], // blacksmithing [num]
- 27 => [FILTER_CR_CALLBACK, 'cbProfession', 333, null], // enchanting [num]
- 28 => [FILTER_CR_CALLBACK, 'cbProfession', 202, null], // engineering [num]
- 29 => [FILTER_CR_CALLBACK, 'cbProfession', 182, null], // herbalism [num]
- 30 => [FILTER_CR_CALLBACK, 'cbProfession', 773, null], // inscription [num]
- 31 => [FILTER_CR_CALLBACK, 'cbProfession', 755, null], // jewelcrafting [num]
- 32 => [FILTER_CR_CALLBACK, 'cbProfession', 165, null], // leatherworking [num]
- 33 => [FILTER_CR_CALLBACK, 'cbProfession', 186, null], // mining [num]
- 34 => [FILTER_CR_CALLBACK, 'cbProfession', 393, null], // skinning [num]
- 35 => [FILTER_CR_CALLBACK, 'cbProfession', 197, null], // tailoring [num]
- 36 => [FILTER_CR_CALLBACK, 'cbHasGuild', null, null] // hasguild [yn]
+ 2 => [FILTER_CR_NUMERIC, 'gearscore', NUM_CAST_INT ], // gearscore [num]
+ 3 => [FILTER_CR_CALLBACK, 'cbAchievs', null, null], // achievementpoints [num]
+ 5 => [FILTER_CR_NUMERIC, 'talenttree1', NUM_CAST_INT ], // talenttree1 [num]
+ 6 => [FILTER_CR_NUMERIC, 'talenttree2', NUM_CAST_INT ], // talenttree2 [num]
+ 7 => [FILTER_CR_NUMERIC, 'talenttree3', NUM_CAST_INT ], // talenttree3 [num]
+ 9 => [FILTER_CR_STRING, 'g.name', ], // guildname
+ 10 => [FILTER_CR_CALLBACK, 'cbHasGuildRank', null, null], // guildrank
+ 12 => [FILTER_CR_CALLBACK, 'cbTeamName', null, null], // teamname2v2
+ 15 => [FILTER_CR_CALLBACK, 'cbTeamName', null, null], // teamname3v3
+ 18 => [FILTER_CR_CALLBACK, 'cbTeamName', null, null], // teamname5v5
+ 13 => [FILTER_CR_CALLBACK, 'cbTeamRating', null, null], // teamrtng2v2
+ 16 => [FILTER_CR_CALLBACK, 'cbTeamRating', null, null], // teamrtng3v3
+ 19 => [FILTER_CR_CALLBACK, 'cbTeamRating', null, null], // teamrtng5v5
+ 14 => [FILTER_CR_NYI_PH, 0 ], // teamcontrib2v2 [num]
+ 17 => [FILTER_CR_NYI_PH, 0 ], // teamcontrib3v3 [num]
+ 20 => [FILTER_CR_NYI_PH, 0 ], // teamcontrib5v5 [num]
+ 21 => [FILTER_CR_CALLBACK, 'cbWearsItems', null, null], // wearingitem [str]
+ 23 => [FILTER_CR_CALLBACK, 'cbCompletedAcv', null, null], // completedachievement
+ 25 => [FILTER_CR_CALLBACK, 'cbProfession', SKILL_ALCHEMY, null], // alchemy [num]
+ 26 => [FILTER_CR_CALLBACK, 'cbProfession', SKILL_BLACKSMITHING, null], // blacksmithing [num]
+ 27 => [FILTER_CR_CALLBACK, 'cbProfession', SKILL_ENCHANTING, null], // enchanting [num]
+ 28 => [FILTER_CR_CALLBACK, 'cbProfession', SKILL_ENGINEERING, null], // engineering [num]
+ 29 => [FILTER_CR_CALLBACK, 'cbProfession', SKILL_HERBALISM, null], // herbalism [num]
+ 30 => [FILTER_CR_CALLBACK, 'cbProfession', SKILL_INSCRIPTION, null], // inscription [num]
+ 31 => [FILTER_CR_CALLBACK, 'cbProfession', SKILL_JEWELCRAFTING, null], // jewelcrafting [num]
+ 32 => [FILTER_CR_CALLBACK, 'cbProfession', SKILL_LEATHERWORKING, null], // leatherworking [num]
+ 33 => [FILTER_CR_CALLBACK, 'cbProfession', SKILL_MINING, null], // mining [num]
+ 34 => [FILTER_CR_CALLBACK, 'cbProfession', SKILL_SKINNING, null], // skinning [num]
+ 35 => [FILTER_CR_CALLBACK, 'cbProfession', SKILL_TAILORING, null], // tailoring [num]
+ 36 => [FILTER_CR_CALLBACK, 'cbHasGuild', null, null] // hasguild [yn]
);
diff --git a/includes/types/spell.class.php b/includes/types/spell.class.php
index c59f67a2..a2300e58 100644
--- a/includes/types/spell.class.php
+++ b/includes/types/spell.class.php
@@ -2078,7 +2078,7 @@ class SpellList extends BaseType
return $x;
}
- public function getColorsForCurrent()
+ public function getColorsForCurrent() : array
{
$gry = $this->curTpl['skillLevelGrey'];
$ylw = $this->curTpl['skillLevelYellow'];
@@ -2094,7 +2094,7 @@ class SpellList extends BaseType
if ($org >= $ylw || $org >= $grn || $org >= $gry)
$org = 0;
- return $gry > 1 ? [$org, $ylw, $grn, $gry] : null;
+ return $gry > 1 ? [$org, $ylw, $grn, $gry] : [];
}
public function getListviewData($addInfoMask = 0x0)
diff --git a/localization/lang.class.php b/localization/lang.class.php
index cec56e97..324a0f25 100644
--- a/localization/lang.class.php
+++ b/localization/lang.class.php
@@ -437,6 +437,17 @@ class Lang
return implode(', ', $tmp);
}
+ public static function formatSkillBreakpoints(array $bp, bool $html = false) : string
+ {
+ $tmp = Lang::game('difficulty').Lang::main('colon');
+
+ for ($i = 0; $i < 4; $i++)
+ if (!empty($bp[$i]))
+ $tmp .= $html ? ''.$bp[$i].' ' : '[color=r'.($i + 1).']'.$bp[$i].'[/color] ';
+
+ return trim($tmp);
+ }
+
public static function nf($number, $decimals = 0, $no1k = false)
{
// [decimal, thousand]
diff --git a/pages/item.php b/pages/item.php
index e634621a..cb8508a1 100644
--- a/pages/item.php
+++ b/pages/item.php
@@ -286,11 +286,17 @@ class ItemPage extends genericPage
$infobox[] = Lang::item('cantDisenchant');
}
- if (($_flags & ITEM_FLAG_MILLABLE) && $this->subject->getField('requiredSkill') == 773)
+ if (($_flags & ITEM_FLAG_MILLABLE) && $this->subject->getField('requiredSkill') == SKILL_INSCRIPTION)
+ {
$infobox[] = Lang::item('millable').' ([tooltip=tooltip_reqinscription]'.$this->subject->getField('requiredSkillRank').'[/tooltip])';
+ $infobox[] = Lang::formatSkillBreakpoints(Game::getBreakpointsForSkill(SKILL_INSCRIPTION, $this->subject->getField('requiredSkillRank')));
+ }
- if (($_flags & ITEM_FLAG_PROSPECTABLE) && $this->subject->getField('requiredSkill') == 755)
+ if (($_flags & ITEM_FLAG_PROSPECTABLE) && $this->subject->getField('requiredSkill') == SKILL_JEWELCRAFTING)
+ {
$infobox[] = Lang::item('prospectable').' ([tooltip=tooltip_reqjewelcrafting]'.$this->subject->getField('requiredSkillRank').'[/tooltip])';
+ $infobox[] = Lang::formatSkillBreakpoints(Game::getBreakpointsForSkill(SKILL_JEWELCRAFTING, $this->subject->getField('requiredSkillRank')));
+ }
if ($_flags & ITEM_FLAG_DEPRECATED)
$infobox[] = '[tooltip=tooltip_deprecated]'.Lang::item('deprecated').'[/tooltip]';
diff --git a/pages/npc.php b/pages/npc.php
index 9b363c67..34230d9b 100644
--- a/pages/npc.php
+++ b/pages/npc.php
@@ -645,13 +645,13 @@ class NpcPage extends GenericPage
}
// tabs: this creature contains..
- $skinTab = ['tab_skinning', 'skinning'];
+ $skinTab = ['tab_skinning', 'skinning', SKILL_SKINNING];
if ($_typeFlags & NPC_TYPEFLAG_HERBLOOT)
- $skinTab = ['tab_herbalism', 'herbalism'];
+ $skinTab = ['tab_herbalism', 'herbalism', SKILL_HERBALISM];
else if ($_typeFlags & NPC_TYPEFLAG_MININGLOOT)
- $skinTab = ['tab_mining', 'mining'];
+ $skinTab = ['tab_mining', 'mining', SKILL_MINING];
else if ($_typeFlags & NPC_TYPEFLAG_ENGINEERLOOT)
- $skinTab = ['tab_engineering', 'engineering'];
+ $skinTab = ['tab_engineering', 'engineering', SKILL_ENGINEERING];
/*
extraCols: [Listview.extraCols.count, Listview.extraCols.percent, Listview.extraCols.mode],
@@ -726,6 +726,8 @@ class NpcPage extends GenericPage
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];
diff --git a/pages/object.php b/pages/object.php
index 393edf48..9c005702 100644
--- a/pages/object.php
+++ b/pages/object.php
@@ -84,17 +84,20 @@ class ObjectPage extends GenericPage
};
$infobox[] = Lang::npc('react').Lang::main('colon').'[color=q'.$_($this->subject->getField('A')).']A[/color] [color=q'.$_($this->subject->getField('H')).']H[/color]';
- // reqSkill
+ // 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
{
diff --git a/pages/spell.php b/pages/spell.php
index 0767325d..93cfdc7e 100644
--- a/pages/spell.php
+++ b/pages/spell.php
@@ -216,14 +216,7 @@ class SpellPage extends GenericPage
// difficulty
if ($_ = $this->subject->getColorsForCurrent())
- {
- $bar = [];
- for ($i = 0; $i < 4; $i++)
- if ($_[$i])
- $bar[] = '[color=r'.($i + 1).']'.$_[$i].'[/color]';
-
- $infobox[] = Lang::game('difficulty').Lang::main('colon').implode(' ', $bar);
- }
+ $infobox[] = Lang::formatSkillBreakpoints($_);
}
// accquisition.. 10: starter spell; 7: discovery