Adds crafted items, weapon config, generally working
This commit is contained in:
@@ -4,7 +4,8 @@ import { Equipment } from "./components/Equipment";
|
||||
import { SourceList } from "./components/SourceList";
|
||||
import { StateContext } from "./lib/context/StateContext";
|
||||
import { reducer, withLoadingAction } from "./lib/state";
|
||||
import type { ItemId } from "./lib/types";
|
||||
import { WeaponConfig, type ItemId } from "./lib/types";
|
||||
import { WeaponConfigPicker } from "./components/WeaponConfigPicker";
|
||||
|
||||
function App() {
|
||||
const [state, dispatch] = useReducer(withLoadingAction(reducer), "loading");
|
||||
@@ -25,6 +26,7 @@ function App() {
|
||||
state: {
|
||||
equipedItems: [{ id: 219309 as ItemId, quality: "champion" }],
|
||||
bisList: [],
|
||||
weaponConfig: WeaponConfig.TwoHander,
|
||||
},
|
||||
});
|
||||
}
|
||||
@@ -46,11 +48,15 @@ function App() {
|
||||
}}
|
||||
>
|
||||
<h1>WoW Gear Finder</h1>
|
||||
<h2>Config</h2>
|
||||
<WeaponConfigPicker />
|
||||
<h2>Equipment</h2>
|
||||
<Equipment
|
||||
state={state}
|
||||
onEquip={(item) => dispatch({ action: "equipItem", item })}
|
||||
onUnequip={(item) => dispatch({ action: "unequipItem", item })}
|
||||
/>
|
||||
<h2>Upgrades</h2>
|
||||
<SourceList state={state} />
|
||||
</StateContext.Provider>
|
||||
);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { ItemsById } from "../lib/items";
|
||||
import { ItemsById, itemsPerSlot } from "../lib/items";
|
||||
import type { State } from "../lib/state";
|
||||
import { Slots, type Item, type Slot } from "../lib/types";
|
||||
import { ItemLink } from "./ItemLink";
|
||||
@@ -12,16 +12,28 @@ export type Props = {
|
||||
onUnequip: (item: Item) => void;
|
||||
};
|
||||
|
||||
export const Equipment = ({ state, onEquip, onUnequip }: Props) => (
|
||||
export const Equipment = ({ state, onEquip, onUnequip }: Props) => {
|
||||
return (
|
||||
<div>
|
||||
<ItemTypeahead value={null} onSelect={onEquip} />
|
||||
<ItemTypeahead value={null} onSelect={(item) => item && onEquip(item)} />
|
||||
<div className={styles.equipedItems}>
|
||||
{Slots.map((slot) => (
|
||||
<Slot state={state} slot={slot} onUnequip={onUnequip} key={slot} />
|
||||
))}
|
||||
{Slots.map((slot) => {
|
||||
if (itemsPerSlot(slot, state.weaponConfig) > 0) {
|
||||
return (
|
||||
<Slot
|
||||
state={state}
|
||||
slot={slot}
|
||||
onUnequip={onUnequip}
|
||||
key={slot}
|
||||
/>
|
||||
);
|
||||
}
|
||||
return null;
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
);
|
||||
};
|
||||
|
||||
type SlotProps = {
|
||||
state: State;
|
||||
|
||||
7
src/components/ItemLink.module.css
Normal file
7
src/components/ItemLink.module.css
Normal file
@@ -0,0 +1,7 @@
|
||||
.itemLink {
|
||||
display: inline-flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
font-size: 20px;
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
import type { Item } from "../lib/types";
|
||||
import * as styles from "./ItemLink.module.css";
|
||||
|
||||
export type Props = {
|
||||
item: Item;
|
||||
@@ -9,7 +10,13 @@ export const ItemLink = ({ item }: Props) => (
|
||||
href={`https://www.wowhead.com/item=${item.id}`}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className={styles.itemLink}
|
||||
>
|
||||
{item.icon && (
|
||||
<img
|
||||
src={`https://wow.zamimg.com/images/wow/icons/medium/${item.icon}.jpg`}
|
||||
/>
|
||||
)}
|
||||
{item.name}
|
||||
</a>
|
||||
);
|
||||
|
||||
@@ -5,8 +5,8 @@ import {
|
||||
ComboboxOptions,
|
||||
} from "@headlessui/react";
|
||||
import { useState } from "react";
|
||||
import { AllItems } from "../lib/items";
|
||||
import type { Item } from "../lib/types";
|
||||
import { AllItems } from "../lib/drops";
|
||||
|
||||
export type Props = {
|
||||
value: Item | null;
|
||||
|
||||
@@ -16,9 +16,9 @@ export const QualitySelector = ({ item }: Props) => {
|
||||
});
|
||||
};
|
||||
return (
|
||||
<select onChange={handleOnChange}>
|
||||
<select onChange={handleOnChange} value={item.quality}>
|
||||
{Object.values(Quality).map((quality) => (
|
||||
<option value={quality} selected={item.quality === quality}>
|
||||
<option value={quality}>
|
||||
{quality.charAt(0).toUpperCase() + quality.slice(1)}
|
||||
</option>
|
||||
))}
|
||||
|
||||
@@ -3,3 +3,7 @@
|
||||
gap: 8px;
|
||||
grid-template-columns: 1fr 6em 6em 6em;
|
||||
}
|
||||
|
||||
.itemList {
|
||||
grid-column: 1 / span 4;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import * as _ from "lodash";
|
||||
import _ from "lodash";
|
||||
import { useState } from "react";
|
||||
import { getUpgrades, hasUpgradeType } from "../lib/items";
|
||||
import type { State } from "../lib/state";
|
||||
import {
|
||||
@@ -7,6 +8,7 @@ import {
|
||||
type Source,
|
||||
type Upgrade,
|
||||
} from "../lib/types";
|
||||
import { ItemLink } from "./ItemLink";
|
||||
import styles from "./SourceList.module.css";
|
||||
|
||||
export type Props = {
|
||||
@@ -14,12 +16,12 @@ export type Props = {
|
||||
};
|
||||
|
||||
export const SourceList = ({ state }: Props) => {
|
||||
const upgrades = getUpgrades(state.equipedItems);
|
||||
const upgrades = getUpgrades(state.equipedItems, state.weaponConfig);
|
||||
|
||||
const upgradesBySource: Record<Source, Upgrade[]> = _.groupBy(
|
||||
const upgradesBySource = _.groupBy(
|
||||
upgrades,
|
||||
(upgrade: { item: Item }) => upgrade.item.source,
|
||||
);
|
||||
) as Record<Source, Upgrade[]>;
|
||||
|
||||
return (
|
||||
<div className={styles.sourceList}>
|
||||
@@ -28,22 +30,74 @@ export const SourceList = ({ state }: Props) => {
|
||||
<div>Hero</div>
|
||||
<div>Myth</div>
|
||||
{Object.entries(upgradesBySource).map(
|
||||
([source, items]: [string, Upgrade[]]) => {
|
||||
const champion = items.filter(
|
||||
hasUpgradeType(UpgradeType.Champion),
|
||||
).length;
|
||||
const hero = items.filter(hasUpgradeType(UpgradeType.Hero)).length;
|
||||
const myth = items.filter(hasUpgradeType(UpgradeType.Myth)).length;
|
||||
return (
|
||||
<>
|
||||
<div>{source}</div>
|
||||
<div>{champion}</div>
|
||||
<div>{hero}</div>
|
||||
<div>{myth}</div>
|
||||
</>
|
||||
);
|
||||
},
|
||||
([source, items]: [string, Upgrade[]]) => (
|
||||
<SourceInfo source={source as Source} items={items} key={source} />
|
||||
),
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
type SourceInfoProps = {
|
||||
source: Source;
|
||||
items: Upgrade[];
|
||||
};
|
||||
|
||||
const SourceInfo = ({ source, items }: SourceInfoProps) => {
|
||||
const [upgradeTypeShown, setUpgradeTypeShown] = useState<UpgradeType | null>(
|
||||
null,
|
||||
);
|
||||
|
||||
const championUpgrades = items.filter(hasUpgradeType(UpgradeType.Champion));
|
||||
const heroUpgrades = items.filter(hasUpgradeType(UpgradeType.Hero));
|
||||
const mythUpgrades = items.filter(hasUpgradeType(UpgradeType.Myth));
|
||||
|
||||
const toggleIsOpen = (upgradeType: UpgradeType) => {
|
||||
if (upgradeTypeShown === upgradeType) {
|
||||
setUpgradeTypeShown(null);
|
||||
}
|
||||
setUpgradeTypeShown(upgradeType);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<div>{source}</div>
|
||||
<div onClick={() => toggleIsOpen(UpgradeType.Champion)}>
|
||||
{championUpgrades.length}
|
||||
</div>
|
||||
<div onClick={() => toggleIsOpen(UpgradeType.Hero)}>
|
||||
{heroUpgrades.length}
|
||||
</div>
|
||||
<div onClick={() => toggleIsOpen(UpgradeType.Myth)}>
|
||||
{mythUpgrades.length}
|
||||
</div>
|
||||
{upgradeTypeShown === UpgradeType.Champion && (
|
||||
<div className={styles.itemList}>
|
||||
{championUpgrades.map((upgrade) => (
|
||||
<div>
|
||||
<ItemLink item={upgrade.item} /> ({upgrade.item.slot})
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
{upgradeTypeShown === UpgradeType.Hero && (
|
||||
<div className={styles.itemList}>
|
||||
{heroUpgrades.map((upgrade) => (
|
||||
<div>
|
||||
<ItemLink item={upgrade.item} />
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
{upgradeTypeShown === UpgradeType.Myth && (
|
||||
<div className={styles.itemList}>
|
||||
{mythUpgrades.map((upgrade) => (
|
||||
<div>
|
||||
<ItemLink item={upgrade.item} />
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
28
src/components/WeaponConfigPicker.tsx
Normal file
28
src/components/WeaponConfigPicker.tsx
Normal file
@@ -0,0 +1,28 @@
|
||||
import { useAppState } from "../lib/context/StateContext";
|
||||
import { WeaponConfig } from "../lib/types";
|
||||
|
||||
export const WeaponConfigPicker = () => {
|
||||
const { state, dispatch } = useAppState();
|
||||
|
||||
const handleOnChange = (event: React.ChangeEvent<HTMLSelectElement>) => {
|
||||
const value = event.target.value?.trim();
|
||||
if (value && value.length > 0) {
|
||||
dispatch({
|
||||
action: "changeWeaponConfig",
|
||||
weaponConfig: value as WeaponConfig,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
<select onChange={handleOnChange} value={state.weaponConfig}>
|
||||
{Object.values(WeaponConfig).map((config) => (
|
||||
<option key={config} value={config}>
|
||||
{config}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -12,6 +12,7 @@ export const Aldani = [
|
||||
slot: "trinket",
|
||||
name: "Incorporeal Warpclaw",
|
||||
source: "aldani" as const,
|
||||
icon: "inv_misc_nightsaberclaw_mana",
|
||||
},
|
||||
{
|
||||
id: 242481 as ItemId,
|
||||
|
||||
16
src/lib/drops/crafted.ts
Normal file
16
src/lib/drops/crafted.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
import type { Item } from "../types";
|
||||
|
||||
export const Crafted: Item[] = [
|
||||
{
|
||||
id: 219334,
|
||||
name: "Rune-Branded Armguards",
|
||||
slot: "wrist",
|
||||
source: "crafted",
|
||||
},
|
||||
{
|
||||
id: 219502,
|
||||
name: "Adrenal Surge Clasp",
|
||||
slot: "waist",
|
||||
source: "crafted",
|
||||
},
|
||||
] as Item[];
|
||||
@@ -73,4 +73,18 @@ export const Dawnbreaker = [
|
||||
name: "Bludgeons of Blistering Wind",
|
||||
source: "dawnbreaker" as const,
|
||||
},
|
||||
{
|
||||
id: 221136,
|
||||
slot: "finger",
|
||||
name: "Devout Zealot's Ring",
|
||||
source: "dawnbreaker" as const,
|
||||
icon: "inv_11_0_nerubian_ring_01_color4",
|
||||
},
|
||||
{
|
||||
id: 221141,
|
||||
slot: "finger",
|
||||
name: "High Nerubian Signet",
|
||||
source: "dawnbreaker" as const,
|
||||
icon: "inv_11_0_nerubian_ring_02_color5",
|
||||
},
|
||||
] as Item[];
|
||||
|
||||
@@ -7,6 +7,12 @@ export const Floodgate = [
|
||||
name: "Improvised Seaforium Pacemaker",
|
||||
source: "floodgate" as const,
|
||||
},
|
||||
{
|
||||
id: 232543 as ItemId,
|
||||
slot: "trinket",
|
||||
name: "Ringing Ritual Mud",
|
||||
source: "floodgate" as const,
|
||||
},
|
||||
{
|
||||
id: 234499 as ItemId,
|
||||
slot: "wrist",
|
||||
|
||||
@@ -1,8 +1,23 @@
|
||||
export { Aldani } from "./aldani";
|
||||
export { AraKara } from "./ara-kara";
|
||||
export { Dawnbreaker } from "./dawnbreaker";
|
||||
export { Floodgate } from "./floodgate";
|
||||
export { HallsOfAtonement } from "./halls-of-atonement";
|
||||
export { PrioryOfTheSacredFlame } from "./priory";
|
||||
export { Streets, Gambit } from "./tazavesh";
|
||||
export { ManaforgeOmega } from "./manaforge-omega";
|
||||
import type { Item } from "../types";
|
||||
import { Aldani } from "./aldani";
|
||||
import { AraKara } from "./ara-kara";
|
||||
import { Crafted } from "./crafted";
|
||||
import { Dawnbreaker } from "./dawnbreaker";
|
||||
import { Floodgate } from "./floodgate";
|
||||
import { HallsOfAtonement } from "./halls-of-atonement";
|
||||
import { ManaforgeOmega } from "./manaforge-omega";
|
||||
import { PrioryOfTheSacredFlame } from "./priory";
|
||||
import { Streets, Gambit } from "./tazavesh";
|
||||
|
||||
export const AllItems: Item[] = [
|
||||
...PrioryOfTheSacredFlame,
|
||||
...Aldani,
|
||||
...AraKara,
|
||||
...Dawnbreaker,
|
||||
...Floodgate,
|
||||
...HallsOfAtonement,
|
||||
...Streets,
|
||||
...Gambit,
|
||||
...ManaforgeOmega,
|
||||
...Crafted,
|
||||
];
|
||||
|
||||
@@ -37,4 +37,167 @@ export const ManaforgeOmega = [
|
||||
slot: "back",
|
||||
source: "manaforge omega",
|
||||
},
|
||||
{
|
||||
id: 242395,
|
||||
name: "Astral Antenna",
|
||||
slot: "trinket",
|
||||
source: "manaforge omega",
|
||||
},
|
||||
{
|
||||
id: 243306,
|
||||
name: "Interloper's Reinforced Sandals",
|
||||
slot: "feet",
|
||||
source: "manaforge omega",
|
||||
},
|
||||
{
|
||||
id: 242397,
|
||||
name: "Sigil of the Cosmic Hunt",
|
||||
slot: "trinket",
|
||||
source: "manaforge omega",
|
||||
},
|
||||
{
|
||||
id: 242394,
|
||||
name: "Eradicating Arcanocore",
|
||||
slot: "trinket",
|
||||
source: "manaforge omega",
|
||||
},
|
||||
{
|
||||
id: 237562,
|
||||
name: "Time-Compressed Wristguards",
|
||||
slot: "wrist",
|
||||
source: "manaforge omega",
|
||||
},
|
||||
{
|
||||
id: 237739,
|
||||
name: "Obliteration Beamglaive",
|
||||
slot: "2h-weapon",
|
||||
source: "manaforge omega",
|
||||
},
|
||||
{
|
||||
id: 242391,
|
||||
name: "Soulbinder's Embrace",
|
||||
slot: "trinket",
|
||||
source: "manaforge omega",
|
||||
},
|
||||
{
|
||||
id: 237734,
|
||||
name: "Oath-Breaker's Recompense",
|
||||
slot: "1h-weapon",
|
||||
source: "manaforge omega",
|
||||
},
|
||||
{
|
||||
id: 237726,
|
||||
name: "Marvel of Technomancy",
|
||||
slot: "2h-weapon",
|
||||
source: "manaforge omega",
|
||||
},
|
||||
{
|
||||
id: 237533,
|
||||
name: "Atomic Phasebelt",
|
||||
slot: "waist",
|
||||
source: "manaforge omega",
|
||||
},
|
||||
{
|
||||
id: 237738,
|
||||
name: "Unbound Training Claws",
|
||||
slot: "1h-weapon",
|
||||
source: "manaforge omega",
|
||||
},
|
||||
{
|
||||
id: 238027,
|
||||
name: "Harvested Creephide Cord",
|
||||
slot: "waist",
|
||||
source: "manaforge omega",
|
||||
},
|
||||
{
|
||||
id: 238031,
|
||||
name: "Veiled Manta Vest",
|
||||
slot: "chest",
|
||||
source: "manaforge omega",
|
||||
},
|
||||
{
|
||||
id: 237813,
|
||||
name: "Factory-Issue Plexhammer",
|
||||
slot: "1h-weapon",
|
||||
source: "manaforge omega",
|
||||
},
|
||||
{
|
||||
id: 237565,
|
||||
name: "Kinetic Dunerunners",
|
||||
slot: "feet",
|
||||
source: "manaforge omega",
|
||||
},
|
||||
{
|
||||
id: 237525,
|
||||
name: "Irradiated Impurity Filter",
|
||||
slot: "head",
|
||||
source: "manaforge omega",
|
||||
},
|
||||
{
|
||||
id: 237541,
|
||||
name: "Darksorrow's Corrupted Carapace",
|
||||
slot: "chest",
|
||||
source: "manaforge omega",
|
||||
},
|
||||
{
|
||||
id: 237731,
|
||||
name: "Ergospheric Cudgel",
|
||||
slot: "1h-weapon",
|
||||
source: "manaforge omega",
|
||||
},
|
||||
{
|
||||
id: 237552,
|
||||
name: "Deathbound Shoulderpads",
|
||||
slot: "shoulder",
|
||||
source: "manaforge omega",
|
||||
},
|
||||
{
|
||||
id: 237546,
|
||||
name: "Bindings of Lost Essence",
|
||||
slot: "wrist",
|
||||
source: "manaforge omega",
|
||||
},
|
||||
{
|
||||
id: 237540,
|
||||
name: "Winged Gamma Handlers",
|
||||
slot: "hands",
|
||||
source: "manaforge omega",
|
||||
},
|
||||
{
|
||||
id: 237557,
|
||||
name: "Reaper's Dreadbelt",
|
||||
slot: "waist",
|
||||
source: "manaforge omega",
|
||||
},
|
||||
{
|
||||
id: 237553,
|
||||
name: "Laboratory Test Slippers",
|
||||
slot: "feet",
|
||||
source: "manaforge omega",
|
||||
},
|
||||
{
|
||||
id: 237531,
|
||||
name: "Elite Shadowguard Legwraps",
|
||||
slot: "legs",
|
||||
source: "manaforge omega",
|
||||
},
|
||||
{
|
||||
id: 230026,
|
||||
name: "Scrapfield 9001",
|
||||
slot: "trinket",
|
||||
source: "manaforge omega",
|
||||
},
|
||||
{
|
||||
id: 228854,
|
||||
name: "Bilgerat's Discarded Slacks",
|
||||
slot: "legs",
|
||||
source: "manaforge omega",
|
||||
},
|
||||
{
|
||||
id: 242401,
|
||||
name: "Brand of Ceaseless Ire",
|
||||
slot: "trinket",
|
||||
source: "manaforge omega",
|
||||
icon: "inv_112_raidtrinkets_manaforgetanktrinket3",
|
||||
},
|
||||
] as const;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import _ from "lodash";
|
||||
import {
|
||||
UpgradeType,
|
||||
WeaponConfig,
|
||||
type EquipedItem,
|
||||
type Item,
|
||||
type ItemId,
|
||||
@@ -8,29 +9,7 @@ import {
|
||||
type Slot,
|
||||
type Upgrade,
|
||||
} from "./types";
|
||||
import {
|
||||
Aldani,
|
||||
AraKara,
|
||||
Dawnbreaker,
|
||||
Floodgate,
|
||||
HallsOfAtonement,
|
||||
PrioryOfTheSacredFlame,
|
||||
Streets,
|
||||
Gambit,
|
||||
ManaforgeOmega,
|
||||
} from "./drops";
|
||||
|
||||
export const AllItems: Item[] = [
|
||||
...PrioryOfTheSacredFlame,
|
||||
...Aldani,
|
||||
...AraKara,
|
||||
...Dawnbreaker,
|
||||
...Floodgate,
|
||||
...HallsOfAtonement,
|
||||
...Streets,
|
||||
...Gambit,
|
||||
...ManaforgeOmega,
|
||||
];
|
||||
import { AllItems } from "./drops";
|
||||
|
||||
export const ItemsById: Record<string, Item> = AllItems.reduce(
|
||||
(db, item) => {
|
||||
@@ -87,7 +66,10 @@ export const upgradesForItem = ({ quality }: { quality: Quality }) => {
|
||||
}
|
||||
};
|
||||
|
||||
export const getUpgrades = (equipedItemIds: EquipedItem[]): Upgrade[] => {
|
||||
export const getUpgrades = (
|
||||
equipedItemIds: EquipedItem[],
|
||||
weaponConfig: WeaponConfig,
|
||||
): Upgrade[] => {
|
||||
return Object.entries(ItemsBySlot).flatMap(([slot, items]) => {
|
||||
const equipedItems = equipedItemIds.flatMap(({ id, quality }) => ({
|
||||
item: ItemsById[id],
|
||||
@@ -96,7 +78,16 @@ export const getUpgrades = (equipedItemIds: EquipedItem[]): Upgrade[] => {
|
||||
const equipedInSlot = equipedItems.filter(
|
||||
(item) => item.item.slot === slot,
|
||||
);
|
||||
const itemsForSlot = itemsPerSlot(slot as Slot);
|
||||
|
||||
const itemsForSlot = itemsPerSlot(slot as Slot, weaponConfig);
|
||||
|
||||
if (itemsForSlot === 0) {
|
||||
console.log(
|
||||
`Skipping ${slot} because it has no items with ${weaponConfig}`,
|
||||
);
|
||||
return [];
|
||||
}
|
||||
|
||||
const numChampion = equipedInSlot.filter(qualityAtLeast("champion")).length;
|
||||
const numHero = equipedInSlot.filter(qualityAtLeast("hero")).length;
|
||||
const numMyth = equipedInSlot.filter(qualityAtLeast("myth")).length;
|
||||
@@ -105,7 +96,6 @@ export const getUpgrades = (equipedItemIds: EquipedItem[]): Upgrade[] => {
|
||||
numHero >= itemsForSlot ? [] : [UpgradeType.Hero],
|
||||
numMyth >= itemsForSlot ? [] : [UpgradeType.Myth],
|
||||
].flat();
|
||||
console.log(slot, numChampion, numHero, numMyth, upgradeTypes);
|
||||
return items.map((item) => {
|
||||
const equiped = equipedInSlot.find((i) => i.item.id === item.id);
|
||||
if (equiped) {
|
||||
@@ -128,12 +118,27 @@ export const hasUpgradeType =
|
||||
(upgrade: Upgrade): boolean =>
|
||||
upgrade.upgradeTypes.includes(type);
|
||||
|
||||
export const itemsPerSlot = (slot: Slot): number => {
|
||||
export const itemsPerSlot = (
|
||||
slot: Slot,
|
||||
weaponConfig: WeaponConfig,
|
||||
): number => {
|
||||
switch (slot) {
|
||||
case "trinket":
|
||||
case "finger":
|
||||
case "1h-weapon":
|
||||
return 2;
|
||||
case "1h-weapon":
|
||||
switch (weaponConfig) {
|
||||
case WeaponConfig.DualWield:
|
||||
return 2;
|
||||
case WeaponConfig.TwoHander:
|
||||
return 0;
|
||||
default:
|
||||
return 1;
|
||||
}
|
||||
case "off-hand":
|
||||
return weaponConfig === WeaponConfig.WeaponAndOffHand ? 1 : 0;
|
||||
case "2h-weapon":
|
||||
return weaponConfig === WeaponConfig.TwoHander ? 1 : 0;
|
||||
default:
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -1,13 +1,16 @@
|
||||
import type { EquipedItem, Item, ItemId, Quality } from "./types";
|
||||
import { WeaponConfig } from "./types";
|
||||
|
||||
export type State = {
|
||||
equipedItems: EquipedItem[];
|
||||
bisList: ItemId[];
|
||||
weaponConfig: WeaponConfig;
|
||||
};
|
||||
|
||||
export const emptyState: State = {
|
||||
equipedItems: [],
|
||||
bisList: [],
|
||||
weaponConfig: WeaponConfig.TwoHander, // Default weapon config
|
||||
};
|
||||
|
||||
export type Action =
|
||||
@@ -23,6 +26,10 @@ export type Action =
|
||||
action: "changeQuality";
|
||||
itemId: ItemId;
|
||||
quality: Quality;
|
||||
}
|
||||
| {
|
||||
action: "changeWeaponConfig";
|
||||
weaponConfig: WeaponConfig;
|
||||
};
|
||||
|
||||
export const reducer = (state: State, action: Action): State => {
|
||||
@@ -58,6 +65,11 @@ export const reducer = (state: State, action: Action): State => {
|
||||
return item;
|
||||
}),
|
||||
};
|
||||
case "changeWeaponConfig":
|
||||
return {
|
||||
...state,
|
||||
weaponConfig: action.weaponConfig,
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ export const Slots = [
|
||||
"trinket",
|
||||
"1h-weapon",
|
||||
"2h-weapon",
|
||||
"shield",
|
||||
"off-hand",
|
||||
] as const;
|
||||
|
||||
export type Slot = (typeof Slots)[number];
|
||||
@@ -39,13 +39,15 @@ export type Source =
|
||||
| "halls-of-atonement"
|
||||
| "priory"
|
||||
| "streets"
|
||||
| "manaforge omega";
|
||||
| "manaforge omega"
|
||||
| "crafted";
|
||||
|
||||
export type Item = {
|
||||
id: ItemId;
|
||||
slot: Slot;
|
||||
name: string;
|
||||
source: Source;
|
||||
icon?: string; // Wowhead icon name (not ID)
|
||||
};
|
||||
|
||||
export type EquipedItem = {
|
||||
@@ -64,3 +66,11 @@ export const UpgradeType = {
|
||||
export type UpgradeType = (typeof UpgradeType)[keyof typeof UpgradeType];
|
||||
|
||||
export type Upgrade = { item: Item; upgradeTypes: UpgradeType[] };
|
||||
|
||||
export const WeaponConfig = {
|
||||
WeaponAndOffHand: "weapon and off-hand",
|
||||
DualWield: "dual wield",
|
||||
TwoHander: "two-hander",
|
||||
};
|
||||
|
||||
export type WeaponConfig = (typeof WeaponConfig)[keyof typeof WeaponConfig];
|
||||
|
||||
Reference in New Issue
Block a user