diff --git a/src/components/artifact-modifiers.tsx b/src/components/artifact-modifiers.tsx index 5794199..2eb70dd 100644 --- a/src/components/artifact-modifiers.tsx +++ b/src/components/artifact-modifiers.tsx @@ -10,7 +10,7 @@ import { export function getArtifactModifier( artifact: ArtifactItem, - type: ArtifactAttributeType + type: ArtifactAttributeType, ): ArtifactAttribute | undefined { const modifiers = modifiersForArtifact(artifact); const modifier = modifiers.find((mod) => mod.type === type); @@ -18,7 +18,7 @@ export function getArtifactModifier( } export function modifiersForArtifact( - artifact: ArtifactItem + artifact: ArtifactItem, ): ArtifactAttribute[] { const artifactBuffs: ArtifactAttribute[] = [ artifact.attributes.namePrefix, @@ -38,7 +38,7 @@ export function modifiersForArtifact( } function modifierText(modifier: ArtifactAttribute): string { - const percentage = `${modifier.magnitude * 100 - 100}%`; + const percentage = `${Math.round((modifier.magnitude - 1) * 1000) / 10}%`; switch (modifier.type) { case ArtifactAttributeType.BonusStrength: return `${percentage} increased strength`; @@ -109,18 +109,28 @@ function modifierText(modifier: ArtifactAttribute): string { export function ArtifactModifiers({ artifact, sx, + children, }: { artifact: ArtifactItem; sx?: any; + children?: JSX.Element; }): JSX.Element { const modifiers = modifiersForArtifact(artifact); + let decorator = (text: string, key: string) => ( + + {text} + + ); + + if (typeof children === "function") { + decorator = children; + } + return ( - {modifiers.map((modifier) => ( - - {modifierText(modifier)} - - ))} + {modifiers.map((modifier) => + decorator(modifierText(modifier), modifier.type), + )} ); } diff --git a/src/game/inventory.tsx b/src/game/inventory.tsx deleted file mode 100644 index ca3afe5..0000000 --- a/src/game/inventory.tsx +++ /dev/null @@ -1,356 +0,0 @@ -import React, { useState, useEffect } from "react"; - -import Grid from "@mui/material/Grid"; -import List from "@mui/material/List"; -import ListItem from "@mui/material/ListItem"; -import ListItemText from "@mui/material/ListItemText"; -import FormControl from "@mui/material/FormControl"; -import InputLabel from "@mui/material/InputLabel"; -import Select from "@mui/material/Select"; -import MenuItem from "@mui/material/MenuItem"; -import Typography from "@mui/material/Typography"; -import Tooltip from "@mui/material/Tooltip"; - -import { useHero } from "src/hooks/use-hero"; -import { useDelay } from "src/hooks/use-delay"; -import { - Hero, - InventoryItem, - InventoryItemType, - useEquipItemMutation, - EnchantmentType, -} from "src/generated/graphql"; - -import { - itemSorter, - itemDisplayName, - itemAllowsRebirth, - itemAllowsCrafting, - itemAllowsVoidTravel, - getEnchantmentDisplay, -} from "src/helpers"; -import { RebirthMenu } from "./rebirth"; -import { CreaftingMenu } from "./crafting"; -import { VoidTravelMenu } from "./void-travel"; - -type Slots = - | "leftHand" - | "rightHand" - | "bodyArmor" - | "handArmor" - | "legArmor" - | "headArmor" - | "footArmor"; - -function canEquipTo(slot: Slots, item: InventoryItem): boolean { - switch (slot) { - case "rightHand": - case "leftHand": - if ( - item.type === InventoryItemType.MeleeWeapon || - item.type === InventoryItemType.RangedWeapon || - item.type === InventoryItemType.Shield || - item.type === InventoryItemType.SpellFocus - ) { - return true; - } - return false; - break; - - case "bodyArmor": - if (item.type === InventoryItemType.BodyArmor) { - return true; - } - return false; - break; - case "handArmor": - if (item.type === InventoryItemType.HandArmor) { - return true; - } - return false; - break; - case "legArmor": - if (item.type === InventoryItemType.LegArmor) { - return true; - } - return false; - break; - case "headArmor": - if (item.type === InventoryItemType.HeadArmor) { - return true; - } - return false; - break; - case "footArmor": - if (item.type === InventoryItemType.FootArmor) { - return true; - } - return false; - break; - // case "accessory": - default: - return false; - break; - } -} - -function EquipmentSlot({ - hero, - slot, - label, - onEquip, - disabled, -}: { - hero: Hero; - slot: Slots; - label: string; - onEquip: (slot: Slots, id: string) => void; - disabled: boolean; -}): JSX.Element { - const equipped = hero.equipment[slot]?.id; - - const otherEquipmentSlots: Slots[] = [ - "leftHand", - "rightHand", - "bodyArmor", - "handArmor", - "legArmor", - "headArmor", - "footArmor", - ]; - const otherEquippedItems = otherEquipmentSlots - .filter( - (otherSlot: Slots) => hero.equipment[otherSlot] && otherSlot !== slot, - ) - .map((otherSlot: Slots) => hero.equipment[otherSlot]?.id); - - const [value, setValue] = useState(equipped || ""); - const items = hero.inventory - .filter((item) => canEquipTo(slot, item)) - .sort(itemSorter); - - return ( - -
- - {label} - - -
-
- ); -} - -function QuestItems({ - hero, - disabled, - onChange, -}: { - hero: Hero; - disabled: boolean; - onChange?: (val: string) => void; -}): JSX.Element { - const [value, setValue] = useState(""); - const items = hero.inventory - .filter((item) => item.type === InventoryItemType.Quest) - // higher level quest items first! - .sort((a, b) => b.level - a.level); - - const label = "Quest items (passive, always active)"; - - const existingItem = hero.inventory.find((item) => item.id === value); - - useEffect(() => { - if (value.length && !existingItem) { - setValue(""); - } - }, [existingItem, value]); - - return ( - -
- - {label} - - -
-
- ); -} - -export function Inventory(): JSX.Element | null { - const [currentDelay] = useDelay(); - const [equipItemMutation, { loading }] = useEquipItemMutation(); - const [selectedQuestItem, setSelectedQuestItem] = useState(""); - const hero = useHero(); - - const shouldDisable = loading || currentDelay > 0; - - if (!hero) { - return null; - } - - async function handleEquip(slot: Slots, item: string) { - try { - await equipItemMutation({ - variables: { - item, - slot, - }, - }); - } catch (e) {} - } - - return ( - - - - Equipped items - - - - - - - - - - - - - - - - - - - - - - - - - - {hero.level === hero.levelCap && itemAllowsRebirth(selectedQuestItem) && ( - - - - )} - {itemAllowsCrafting(selectedQuestItem) && ( - - - - )} - {itemAllowsVoidTravel(selectedQuestItem) && ( - - - - )} - - - ); -} diff --git a/src/game/inventory/equipment-slot.tsx b/src/game/inventory/equipment-slot.tsx new file mode 100644 index 0000000..ce1da53 --- /dev/null +++ b/src/game/inventory/equipment-slot.tsx @@ -0,0 +1,151 @@ +import React, { useState, useEffect } from "react"; + +import FormControl from "@mui/material/FormControl"; +import InputLabel from "@mui/material/InputLabel"; +import Select from "@mui/material/Select"; +import MenuItem from "@mui/material/MenuItem"; +import Typography from "@mui/material/Typography"; + +import { + Hero, + InventoryItem, + InventoryItemType, + useEquipItemMutation, + EnchantmentType, +} from "src/generated/graphql"; + +import { + itemSorter, + itemDisplayName, + getEnchantmentDisplay, +} from "src/helpers"; + +import { Slots } from "./types"; + +function canEquipTo(slot: Slots, item: InventoryItem): boolean { + switch (slot) { + case "rightHand": + case "leftHand": + if ( + item.type === InventoryItemType.MeleeWeapon || + item.type === InventoryItemType.RangedWeapon || + item.type === InventoryItemType.Shield || + item.type === InventoryItemType.SpellFocus + ) { + return true; + } + return false; + break; + + case "bodyArmor": + if (item.type === InventoryItemType.BodyArmor) { + return true; + } + return false; + break; + case "handArmor": + if (item.type === InventoryItemType.HandArmor) { + return true; + } + return false; + break; + case "legArmor": + if (item.type === InventoryItemType.LegArmor) { + return true; + } + return false; + break; + case "headArmor": + if (item.type === InventoryItemType.HeadArmor) { + return true; + } + return false; + break; + case "footArmor": + if (item.type === InventoryItemType.FootArmor) { + return true; + } + return false; + break; + // case "accessory": + default: + return false; + break; + } +} + +export function EquipmentSlot({ + hero, + slot, + label, + onEquip, + disabled, +}: { + hero: Hero; + slot: Slots; + label: string; + onEquip: (slot: Slots, id: string) => void; + disabled: boolean; +}): JSX.Element { + const equipped = hero.equipment[slot]?.id; + + const otherEquipmentSlots: Slots[] = [ + "leftHand", + "rightHand", + "bodyArmor", + "handArmor", + "legArmor", + "headArmor", + "footArmor", + ]; + const otherEquippedItems = otherEquipmentSlots + .filter( + (otherSlot: Slots) => hero.equipment[otherSlot] && otherSlot !== slot, + ) + .map((otherSlot: Slots) => hero.equipment[otherSlot]?.id); + + const [value, setValue] = useState(equipped || ""); + const items = hero.inventory + .filter((item) => canEquipTo(slot, item)) + .sort(itemSorter); + + return ( + +
+ + {label} + + +
+
+ ); +} diff --git a/src/game/inventory/index.tsx b/src/game/inventory/index.tsx new file mode 100644 index 0000000..d45f8e5 --- /dev/null +++ b/src/game/inventory/index.tsx @@ -0,0 +1,168 @@ +import React, { useState, useEffect } from "react"; + +import Grid from "@mui/material/Grid"; +import List from "@mui/material/List"; +import ListItem from "@mui/material/ListItem"; +import ListItemText from "@mui/material/ListItemText"; +import FormControl from "@mui/material/FormControl"; +import InputLabel from "@mui/material/InputLabel"; +import Select from "@mui/material/Select"; +import MenuItem from "@mui/material/MenuItem"; +import Typography from "@mui/material/Typography"; +import Tooltip from "@mui/material/Tooltip"; + +import { useHero } from "src/hooks/use-hero"; +import { useDelay } from "src/hooks/use-delay"; +import { + Hero, + InventoryItem, + InventoryItemType, + useEquipItemMutation, + EnchantmentType, +} from "src/generated/graphql"; + +import { + itemDisplayName, + itemAllowsRebirth, + itemAllowsCrafting, + itemAllowsVoidTravel, + getEnchantmentDisplay, +} from "src/helpers"; +import { ArtifactModifiers } from "src/components/artifact-modifiers"; + +import { RebirthMenu } from "../rebirth"; +import { CreaftingMenu } from "../crafting"; +import { VoidTravelMenu } from "../void-travel"; +import { EquipmentSlot } from "./equipment-slot"; +import { Slots } from "./types"; +import { QuestItems } from "./quest-items"; + +export function Inventory(): JSX.Element | null { + const [currentDelay] = useDelay(); + const [equipItemMutation, { loading }] = useEquipItemMutation(); + const [selectedQuestItem, setSelectedQuestItem] = useState(""); + const hero = useHero(); + + const shouldDisable = loading || currentDelay > 0; + + if (!hero) { + return null; + } + + async function handleEquip(slot: Slots, item: string) { + try { + await equipItemMutation({ + variables: { + item, + slot, + }, + }); + } catch (e) {} + } + + return ( + + + + Equipped items + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {hero.equipment.artifact && ( + + Active artifact + + + )} + + + {hero.level === hero.levelCap && itemAllowsRebirth(selectedQuestItem) && ( + + + + )} + {itemAllowsCrafting(selectedQuestItem) && ( + + + + )} + {itemAllowsVoidTravel(selectedQuestItem) && ( + + + + )} + + + ); +} diff --git a/src/game/inventory/quest-items.tsx b/src/game/inventory/quest-items.tsx new file mode 100644 index 0000000..e732a5c --- /dev/null +++ b/src/game/inventory/quest-items.tsx @@ -0,0 +1,76 @@ +import React, { useState, useEffect } from "react"; + +import FormControl from "@mui/material/FormControl"; +import InputLabel from "@mui/material/InputLabel"; +import Select from "@mui/material/Select"; +import MenuItem from "@mui/material/MenuItem"; +import Typography from "@mui/material/Typography"; + +import { Hero, InventoryItem, InventoryItemType } from "src/generated/graphql"; +import { itemDisplayName, getEnchantmentDisplay } from "src/helpers"; + +export function QuestItems({ + hero, + disabled, + onChange, +}: { + hero: Hero; + disabled: boolean; + onChange?: (val: string) => void; +}): JSX.Element { + const [value, setValue] = useState(""); + const items = hero.inventory + .filter((item) => item.type === InventoryItemType.Quest) + // higher level quest items first! + .sort((a, b) => b.level - a.level); + + const label = "Quest items (passive, always active)"; + + const existingItem = hero.inventory.find((item) => item.id === value); + + useEffect(() => { + if (value.length && !existingItem) { + setValue(""); + } + }, [existingItem, value]); + + return ( + +
+ + {label} + + +
+
+ ); +} diff --git a/src/game/inventory/types.ts b/src/game/inventory/types.ts new file mode 100644 index 0000000..7a6521d --- /dev/null +++ b/src/game/inventory/types.ts @@ -0,0 +1,8 @@ +export type Slots = + | "leftHand" + | "rightHand" + | "bodyArmor" + | "handArmor" + | "legArmor" + | "headArmor" + | "footArmor"; diff --git a/src/me.graphql b/src/me.graphql index ba1f985..9e27e7a 100644 --- a/src/me.graphql +++ b/src/me.graphql @@ -186,6 +186,36 @@ query Me { level enchantment } + artifact { + id + owner + name + level + + attributes { + namePrefix { + type + magnitude + } + namePostfix { + type + magnitude + } + titlePrefix { + type + magnitude + } + titlePostfix { + type + magnitude + } + + bonusAffixes { + type + magnitude + } + } + } } combat {