Skip to content

Commit

Permalink
Port item source info from SoD (#4246)
Browse files Browse the repository at this point in the history
* port two SoD updates

* fix icon enum tooltips

* gear picker style tweaks from sod

* fix gem summary tooltips

* fix apl tooltips

* fix improved icon tooltips

* port over improved item source information

* update comment
  • Loading branch information
kayla-glick authored Apr 14, 2024
1 parent 1f33326 commit fd8d3c5
Show file tree
Hide file tree
Showing 5 changed files with 132 additions and 64 deletions.
Binary file added assets/img/alliance.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/img/horde.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
26 changes: 25 additions & 1 deletion proto/ui.proto
Original file line number Diff line number Diff line change
Expand Up @@ -104,12 +104,31 @@ enum DungeonDifficulty {
DifficultyRaid25H = 6;
}

enum RepLevel {
RepLevelUnknown = 0;
RepLevelHated = 1;
RepLevelHostile = 2;
RepLevelUnfriendly = 3;
RepLevelNeutral = 4;
RepLevelFriendly = 5;
RepLevelHonored = 6;
RepLevelRevered = 7;
RepLevelExalted = 8;
}

// TODO: Wotlk Rep Factions
// Use the faction ID for the field index
enum RepFaction {
RepFactionUnknown = 0;
}

message UIItemSource {
oneof source {
CraftedSource crafted = 1;
DropSource drop = 2;
QuestSource quest = 3;
SoldBySource sold_by = 4;
RepSource rep = 5;
}
}
message CraftedSource {
Expand All @@ -132,6 +151,11 @@ message SoldBySource {
string npc_name = 2;
int32 zone_id = 3;
}
message RepSource {
RepFaction rep_faction_id = 1;
RepLevel rep_level = 2;
Faction faction_id = 3;
}

message UIEnchant {
// All enchants have an effect ID. Some also have an item ID, others have a spell ID,
Expand All @@ -142,7 +166,7 @@ message UIEnchant {
int32 item_id = 2; // ID of the enchant "item". Might be 0 if not available.
int32 spell_id = 3; // ID of the enchant "spell". Might be 0 if not available.

string name = 4;
string name = 4;
string icon = 5;

ItemType type = 6; // Which type of item this enchant can be applied to.
Expand Down
129 changes: 86 additions & 43 deletions ui/core/components/gear_picker.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,12 @@ import { setItemQualityCssClass } from '../css_utils';
import { IndividualSimUI } from '../individual_sim_ui.js';
import { Player } from '../player';
import { Class, GemColor, ItemQuality, ItemSlot, ItemSpec, ItemType } from '../proto/common';
import { DatabaseFilters, UIEnchant as Enchant, UIGem as Gem, UIItem as Item } from '../proto/ui.js';
import { DatabaseFilters, RepFaction, UIEnchant as Enchant, UIGem as Gem, UIItem as Item, UIItem_FactionRestriction } from '../proto/ui.js';
import { ActionId } from '../proto_utils/action_id';
import { getEnchantDescription, getUniqueEnchantString } from '../proto_utils/enchants';
import { EquippedItem } from '../proto_utils/equipped_item';
import { gemMatchesSocket, getEmptyGemSocketIconUrl } from '../proto_utils/gems';
import { difficultyNames, professionNames, slotNames } from '../proto_utils/names.js';
import { difficultyNames, professionNames, REP_FACTION_NAMES, REP_LEVEL_NAMES, slotNames } from '../proto_utils/names.js';
import { Stats } from '../proto_utils/stats';
import { Sim } from '../sim.js';
import { SimUI } from '../sim_ui';
Expand Down Expand Up @@ -694,7 +694,7 @@ export class SelectorModal extends BaseModal {
ilist.dispose();
});

tabAnchor.value!.addEventListener('shown.bs.tab', event => {
tabAnchor.value!.addEventListener('shown.bs.tab', _event => {
ilist.sizeRefresh();
});

Expand Down Expand Up @@ -840,30 +840,21 @@ export class ItemList<T> {
title: EP_TOOLTIP,
});

const show1hWeaponsSelector = makeShow1hWeaponsSelector(
this.tabContent.getElementsByClassName('selector-modal-show-1h-weapons')[0] as HTMLElement,
player.sim,
);
const show2hWeaponsSelector = makeShow2hWeaponsSelector(
this.tabContent.getElementsByClassName('selector-modal-show-2h-weapons')[0] as HTMLElement,
player.sim,
);
makeShow1hWeaponsSelector(this.tabContent.getElementsByClassName('selector-modal-show-1h-weapons')[0] as HTMLElement, player.sim);
makeShow2hWeaponsSelector(this.tabContent.getElementsByClassName('selector-modal-show-2h-weapons')[0] as HTMLElement, player.sim);
if (!(label == 'Items' && (slot == ItemSlot.ItemSlotMainHand || (slot == ItemSlot.ItemSlotOffHand && player.getClass() == Class.ClassWarrior)))) {
(this.tabContent.getElementsByClassName('selector-modal-show-1h-weapons')[0] as HTMLElement).style.display = 'none';
(this.tabContent.getElementsByClassName('selector-modal-show-2h-weapons')[0] as HTMLElement).style.display = 'none';
}

makeShowEPValuesSelector(this.tabContent.getElementsByClassName('selector-modal-show-ep-values')[0] as HTMLElement, player.sim);

const showMatchingGemsSelector = makeShowMatchingGemsSelector(
this.tabContent.getElementsByClassName('selector-modal-show-matching-gems')[0] as HTMLElement,
player.sim,
);
makeShowMatchingGemsSelector(this.tabContent.getElementsByClassName('selector-modal-show-matching-gems')[0] as HTMLElement, player.sim);
if (!label.startsWith('Gem')) {
(this.tabContent.getElementsByClassName('selector-modal-show-matching-gems')[0] as HTMLElement).style.display = 'none';
}

const phaseSelector = makePhaseSelector(this.tabContent.getElementsByClassName('selector-modal-phase-selector')[0] as HTMLElement, player.sim);
makePhaseSelector(this.tabContent.getElementsByClassName('selector-modal-phase-selector')[0] as HTMLElement, player.sim);

if (label == 'Items') {
const filtersButton = this.tabContent.getElementsByClassName('selector-modal-filters-button')[0] as HTMLElement;
Expand Down Expand Up @@ -907,7 +898,7 @@ export class ItemList<T> {
);

const removeButton = this.tabContent.getElementsByClassName('selector-modal-remove-button')[0] as HTMLButtonElement;
removeButton.addEventListener('click', event => {
removeButton.addEventListener('click', _event => {
onRemove(TypedEvent.nextEventID());
});

Expand All @@ -928,7 +919,7 @@ export class ItemList<T> {
player.sim.showExperimentalChangeEmitter.on(() => {
simAllButton.hidden = !player.sim.getShowExperimental();
});
simAllButton.addEventListener('click', event => {
simAllButton.addEventListener('click', _event => {
if (simUI instanceof IndividualSimUI) {
const itemSpecs = Array<ItemSpec>();
const isRangedOrTrinket =
Expand Down Expand Up @@ -1222,22 +1213,26 @@ export class ItemList<T> {
}

private getSourceInfo(item: Item, sim: Sim): JSX.Element {
if (!item.sources || item.sources.length == 0) {
return <></>;
}

const makeAnchor = (href: string, inner: string) => {
const makeAnchor = (href: string, inner: string | JSX.Element) => {
return (
<a href={href}>
<a href={href} target="_blank" dataset={{ whtticon: 'false' }}>
<small>{inner}</small>
</a>
);
};

const source = item.sources[0];
if (!item.sources || item.sources.length == 0) {
return <></>;
}

let source = item.sources[0];
if (source.source.oneofKind == 'crafted') {
const src = source.source.crafted;
return makeAnchor(ActionId.makeSpellUrl(src.spellId), professionNames.get(src.profession) ?? 'Unknown');

if (src.spellId) {
return makeAnchor(ActionId.makeSpellUrl(src.spellId), professionNames.get(src.profession) ?? 'Unknown');
}
return makeAnchor(ActionId.makeItemUrl(item.id), professionNames.get(src.profession) ?? 'Unknown');
} else if (source.source.oneofKind == 'drop') {
const src = source.source.drop;
const zone = sim.db.getZone(src.zoneId);
Expand All @@ -1246,30 +1241,78 @@ export class ItemList<T> {
throw new Error('No zone found for item: ' + item);
}

const rtnEl = makeAnchor(ActionId.makeZoneUrl(zone.id), `${zone.name} (${difficultyNames.get(src.difficulty) ?? 'Unknown'})`);

const category = src.category ? ` - ${src.category}` : '';
if (npc) {
rtnEl.appendChild(document.createElement('br'));
rtnEl.appendChild(makeAnchor(ActionId.makeNpcUrl(npc.id), `${npc.name + category}`));
return makeAnchor(
ActionId.makeNpcUrl(npc.id),
<span>
{zone.name} ({difficultyNames.get(src.difficulty) ?? 'Unknown'})
<br />
{npc.name + category}
</span>,
);
} else if (src.otherName) {
/*innerHTML += `
<br>
<a href="${ActionId.makeZoneUrl(zone.id)}"><small>${src.otherName + category}</small></a>
`;*/
} else if (category) {
/*innerHTML += `
<br>
<a href="${ActionId.makeZoneUrl(zone.id)}"><small>${category}</small></a>
`;*/
return makeAnchor(
ActionId.makeZoneUrl(zone.id),
<span>
{zone.name}
<br />
{src.otherName}
</span>,
);
}
return rtnEl;
} else if (source.source.oneofKind == 'quest') {
return makeAnchor(ActionId.makeZoneUrl(zone.id), zone.name);
} else if (source.source.oneofKind == 'quest' && source.source.quest.name) {
const src = source.source.quest;
return makeAnchor(ActionId.makeQuestUrl(src.id), src.name);
return makeAnchor(
ActionId.makeQuestUrl(src.id),
<span>
Quest
{item.factionRestriction == UIItem_FactionRestriction.ALLIANCE_ONLY && (
<img src="/wotlk/assets/img/alliance.png" className="ms-1" width="15" height="15" />
)}
{item.factionRestriction == UIItem_FactionRestriction.HORDE_ONLY && (
<img src="/wotlk/assets/img/horde.png" className="ms-1" width="15" height="15" />
)}
<br />
{src.name}
</span>,
);
} else if ((source = item.sources.find(source => source.source.oneofKind == 'rep') ?? source).source.oneofKind == 'rep') {
const factionNames = item.sources
.filter(source => source.source.oneofKind == 'rep')
.map(source =>
source.source.oneofKind == 'rep' ? REP_FACTION_NAMES[source.source.rep.repFactionId] : REP_FACTION_NAMES[RepFaction.RepFactionUnknown],
);
const src = source.source.rep;
return makeAnchor(
ActionId.makeItemUrl(item.id),
<>
{factionNames.map(name => (
<span>
{name}
{item.factionRestriction == UIItem_FactionRestriction.ALLIANCE_ONLY && (
<img src="/wotlk/assets/img/alliance.png" className="ms-1" width="15" height="15" />
)}
{item.factionRestriction == UIItem_FactionRestriction.HORDE_ONLY && (
<img src="/wotlk/assets/img/horde.png" className="ms-1" width="15" height="15" />
)}
<br />
</span>
))}
<span>{REP_LEVEL_NAMES[src.repLevel]}</span>
</>,
);
} else if (source.source.oneofKind == 'soldBy') {
const src = source.source.soldBy;
return makeAnchor(ActionId.makeNpcUrl(src.npcId), src.npcName);
return makeAnchor(
ActionId.makeNpcUrl(src.npcId),
<span>
Sold by
<br />
{src.npcName}
</span>,
);
}
return <></>;
}
Expand Down
41 changes: 21 additions & 20 deletions ui/core/proto_utils/names.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,6 @@
import {
ArmorType,
Class,
ItemSlot,
Profession,
PseudoStat,
Race,
RangedWeaponType,
Stat,
WeaponType,
} from '../proto/common.js';
import {
DungeonDifficulty,
RaidFilterOption,
SourceFilterOption,
} from '../proto/ui.js';
import { ResourceType } from '../proto/api.js';
import { ArmorType, Class, ItemSlot, Profession, PseudoStat, Race, RangedWeaponType, Stat, WeaponType } from '../proto/common.js';
import { DungeonDifficulty, RaidFilterOption, RepFaction, RepLevel, SourceFilterOption } from '../proto/ui.js';

export const armorTypeNames: Map<ArmorType, string> = new Map([
[ArmorType.ArmorTypeUnknown, 'Unknown'],
Expand Down Expand Up @@ -117,7 +103,7 @@ export function nameToProfession(name: string): Profession {
const lower = name.toLowerCase();
for (const [key, value] of professionNames) {
if (value.toLowerCase() == lower) {
return key
return key;
}
}
return Profession.ProfessionUnknown;
Expand Down Expand Up @@ -226,8 +212,7 @@ export const pseudoStatNames: Map<PseudoStat, string> = new Map([

export function getClassStatName(stat: Stat, playerClass: Class): string {
const statName = statNames.get(stat);
if (!statName)
return 'UnknownStat';
if (!statName) return 'UnknownStat';
if (playerClass == Class.ClassHunter) {
return statName.replace('Melee', 'Ranged');
} else {
Expand Down Expand Up @@ -317,7 +302,7 @@ export const raidNames: Map<RaidFilterOption, string> = new Map([
[RaidFilterOption.RaidVaultOfArchavon, 'Vault of Archavon'],
[RaidFilterOption.RaidUlduar, 'Ulduar'],
[RaidFilterOption.RaidTrialOfTheCrusader, 'Trial of the Crusader'],
[RaidFilterOption.RaidOnyxiasLair, 'Onyxia\'s Lair'],
[RaidFilterOption.RaidOnyxiasLair, "Onyxia's Lair"],
[RaidFilterOption.RaidIcecrownCitadel, 'Icecrown Citadel'],
[RaidFilterOption.RaidRubySanctum, 'Ruby Sanctum'],
]);
Expand All @@ -333,3 +318,19 @@ export const difficultyNames: Map<DungeonDifficulty, string> = new Map([
[DungeonDifficulty.DifficultyRaid25, '25N'],
[DungeonDifficulty.DifficultyRaid25H, '25H'],
]);

export const REP_LEVEL_NAMES: Record<RepLevel, string> = {
[RepLevel.RepLevelUnknown]: 'Unknown',
[RepLevel.RepLevelHated]: 'Hated',
[RepLevel.RepLevelHostile]: 'Hostile',
[RepLevel.RepLevelUnfriendly]: 'Unfriendly',
[RepLevel.RepLevelNeutral]: 'Neutral',
[RepLevel.RepLevelFriendly]: 'Friendly',
[RepLevel.RepLevelHonored]: 'Honored',
[RepLevel.RepLevelRevered]: 'Revered',
[RepLevel.RepLevelExalted]: 'Exalted',
};

export const REP_FACTION_NAMES: Record<RepFaction, string> = {
[RepFaction.RepFactionUnknown]: 'Unknown',
};

0 comments on commit fd8d3c5

Please sign in to comment.