From 116feee778fb6a2c0a9f21fbc8930446ffffd65d Mon Sep 17 00:00:00 2001 From: cwastche Date: Sun, 14 Jul 2024 16:13:14 +0200 Subject: [PATCH] Fix quests rerendering perf --- client/src/hooks/helpers/useQuests.tsx | 485 ++++++++++++++++++ client/src/hooks/helpers/useStamina.tsx | 15 +- client/src/hooks/store/useQuestStore.tsx | 351 +------------ client/src/ui/components/HooksComponent.tsx | 2 - .../construction/SelectPreviewBuilding.tsx | 17 +- .../src/ui/components/military/ArmyList.tsx | 11 +- client/src/ui/components/quest/QuestInfo.tsx | 31 +- client/src/ui/components/quest/QuestList.tsx | 29 +- client/src/ui/components/quest/QuestPanel.tsx | 12 +- client/src/ui/components/quest/utils.tsx | 4 +- .../StructureConstructionMenu.tsx | 3 +- .../modules/navigation/BottomNavigation.tsx | 32 +- .../navigation/LeftNavigationModule.tsx | 29 +- .../navigation/RightNavigationModule.tsx | 19 +- .../navigation/TopMiddleNavigation.tsx | 5 +- .../world-structures/WorldStructuresMenu.tsx | 7 +- 16 files changed, 615 insertions(+), 437 deletions(-) create mode 100644 client/src/hooks/helpers/useQuests.tsx diff --git a/client/src/hooks/helpers/useQuests.tsx b/client/src/hooks/helpers/useQuests.tsx new file mode 100644 index 000000000..1fd4b19fe --- /dev/null +++ b/client/src/hooks/helpers/useQuests.tsx @@ -0,0 +1,485 @@ +import { useCallback, useEffect, useMemo, useState } from "react"; +import { BuildingType, QuestType, StructureType } from "@bibliothecadao/eternum"; +import { useDojo } from "../context/DojoContext"; +import { useEntityQuery } from "@dojoengine/react"; +import { HasValue, getComponentValue, runQuery } from "@dojoengine/recs"; +import { getEntityIdFromKeys } from "@dojoengine/utils"; +import { useEntities } from "./useEntities"; +import { ArmyInfo, useArmiesByEntityOwner } from "./useArmies"; +import { useGetMyOffers } from "./useTrade"; +import { getPillageEvents } from "@/dojo/events/pillageEventQueries"; + +export enum QuestName { + Settle = "Settle", + BuildFarm = "Build a Farm", + BuildResource = "Build a Resource Facility", + CreateTrade = "Create a Trade", + CreateArmy = "Create an Army", + Travel = "Travel with your Army", + BuildWorkersHut = "Build a workers hut", + Market = "Build a market", + Pillage = "Pillage a structure", + Mine = "Discover an earthenshard mine", + Contribution = "Contribute to a hyperstructure", + Hyperstructure = "Build a hyperstructure", +} + +const questPrize = new Map([ + [QuestName.Settle, [{ id: QuestType.Food, title: "Common Resources" }]], + [ + QuestName.BuildFarm, + [ + { id: QuestType.CommonResources, title: "Common Resources" }, + { id: QuestType.UncommonResources, title: "Uncommon Resources" }, + { id: QuestType.RareResources, title: "Rare Resources" }, + { id: QuestType.UniqueResources, title: "Unique Resources" }, + { id: QuestType.LegendaryResources, title: "Legendary Resources" }, + { id: QuestType.MythicResources, title: "Mythic Resources" }, + ], + ], + [QuestName.BuildResource, [{ id: QuestType.Trade, title: "Donkeys and Lords" }]], + [QuestName.CreateTrade, [{ id: QuestType.Military, title: "Claim Starting Army" }]], + [QuestName.CreateArmy, [{ id: QuestType.Earthenshard, title: "Claim Earthen Shard" }]], + [QuestName.Travel, [{ id: QuestType.Travel, title: "Travel" }]], + [QuestName.BuildWorkersHut, [{ id: QuestType.Population, title: "Population" }]], + [QuestName.Market, [{ id: QuestType.Market, title: "Market" }]], + [QuestName.Pillage, [{ id: QuestType.Pillage, title: "Pillage" }]], + [QuestName.Mine, [{ id: QuestType.Mine, title: "Mine" }]], + [QuestName.Contribution, [{ id: QuestType.Contribution, title: "Contribution" }]], + [QuestName.Hyperstructure, [{ id: QuestType.Hyperstructure, title: "Hyperstructure" }]], +]); + +export interface Quest { + name: QuestName; + description: string; + steps: string[]; + prizes: Prize[]; + depth: number; + status: QuestStatus; +} + +export interface Prize { + id: QuestType; + title: string; +} + +export enum QuestStatus { + InProgress, + Completed, + Claimed, +} + +export const useQuests = () => { + const { + buildingQuantities, + entityArmies, + orders, + pillageHistoryLength, + hasTroops, + hasTraveled, + fragmentMines, + hyperstructures, + hyperstructureContributions, + } = useQuestDependencies(); + + const { questClaimStatus } = useQuestClaimStatus(); + + const settleQuest = useMemo(() => { + return { + name: QuestName.Settle, + description: "A gift of food from the gods", + steps: ["Settle your first Realm"], + prizes: questPrize.get(QuestName.Settle) || [], + depth: 0, + status: questClaimStatus[QuestName.Settle] ? QuestStatus.Claimed : QuestStatus.Completed, + }; + }, [questClaimStatus[QuestName.Settle]]); + + const buildFarmQuest = useMemo( + () => ({ + name: QuestName.BuildFarm, + description: "Wheat is the lifeblood of your people. Go to the construction menu and build a farm", + status: + buildingQuantities.farms > 0 + ? questClaimStatus[QuestName.BuildFarm] + ? QuestStatus.Claimed + : QuestStatus.Completed + : QuestStatus.InProgress, + steps: [ + "Navigate to the construction menu", + "Select the 'Farm' card", + "Left click on a hex to build it, or right click to cancel", + ], + prizes: questPrize.get(QuestName.BuildFarm) || [], + depth: 1, + }), + [questClaimStatus[QuestName.BuildFarm], buildingQuantities.farms], + ); + + const buildResourceQuest = useMemo( + () => ({ + name: QuestName.BuildResource, + description: "Eternum thrives on resources. Construct resource facilities to harvest them efficiently", + status: + buildingQuantities.resource > 0 + ? questClaimStatus[QuestName.BuildResource] + ? QuestStatus.Claimed + : QuestStatus.Completed + : QuestStatus.InProgress, + steps: [ + "Navigate to the construction menu", + "Navigate to the 'Resources' tab and select a resource card", + "Left click on a hex to build it, or right click to cancel", + ], + prizes: questPrize.get(QuestName.BuildResource) || [], + depth: 2, + }), + [questClaimStatus[QuestName.BuildResource], buildingQuantities.resource], + ); + + const createTradeQuest = useMemo( + () => ({ + name: QuestName.CreateTrade, + description: "Trading is the lifeblood of Eternum. Create a trade to start your economy", + status: + orders.length > 0 + ? questClaimStatus[QuestName.CreateTrade] + ? QuestStatus.Claimed + : QuestStatus.Completed + : QuestStatus.InProgress, + steps: [], + prizes: questPrize.get(QuestName.CreateTrade) || [], + depth: 3, + }), + [questClaimStatus[QuestName.CreateTrade], orders], + ); + + const createArmyQuest = useMemo( + () => ({ + name: QuestName.CreateArmy, + description: "Conquest is fulfilling. Create an army to conquer your enemies", + status: + entityArmies.length > 0 && hasTroops + ? questClaimStatus[QuestName.CreateArmy] + ? QuestStatus.Claimed + : QuestStatus.Completed + : QuestStatus.InProgress, + steps: ["Create an army to conquer your enemies", "Assign troops to your army"], + prizes: questPrize.get(QuestName.CreateArmy) || [], + depth: 4, + }), + [questClaimStatus[QuestName.CreateArmy], entityArmies, hasTroops], + ); + + const travelQuest = useMemo( + () => ({ + name: QuestName.Travel, + description: "Travel with your army", + status: hasTraveled + ? questClaimStatus[QuestName.Travel] + ? QuestStatus.Claimed + : QuestStatus.Completed + : QuestStatus.InProgress, + steps: ["Go to world view", "Right click on your army", "Travel w/ your army"], + prizes: questPrize.get(QuestName.Travel) || [], + depth: 5, + }), + [questClaimStatus[QuestName.Travel], hasTraveled], + ); + + const buildWorkersHutQuest = useMemo( + () => ({ + name: QuestName.BuildWorkersHut, + description: "Build worker huts to extend your population capacity", + status: + buildingQuantities.workersHut > 0 + ? questClaimStatus[QuestName.BuildWorkersHut] + ? QuestStatus.Claimed + : QuestStatus.Completed + : QuestStatus.InProgress, + steps: [], + prizes: questPrize.get(QuestName.BuildWorkersHut) || [], + depth: 6, + }), + [questClaimStatus[QuestName.BuildWorkersHut], buildingQuantities.workersHut], + ); + + const marketQuest = useMemo( + () => ({ + name: QuestName.Market, + description: "Build a market to produce donkeys. Donkeys are a resource used to transport goods", + status: + buildingQuantities.markets > 0 + ? questClaimStatus[QuestName.Market] + ? QuestStatus.Claimed + : QuestStatus.Completed + : QuestStatus.InProgress, + steps: [], + prizes: questPrize.get(QuestName.Market) || [], + depth: 6, + }), + [questClaimStatus[QuestName.Market], buildingQuantities.markets], + ); + + const pillageQuest = useMemo( + () => ({ + name: QuestName.Pillage, + description: "Pillage a realm, hyperstructure or earthenshard mine", + steps: [], + prizes: questPrize.get(QuestName.Pillage) || [], + depth: 6, + status: + pillageHistoryLength > 0 + ? questClaimStatus[QuestName.Pillage] + ? QuestStatus.Claimed + : QuestStatus.Completed + : QuestStatus.InProgress, + }), + [questClaimStatus[QuestName.Pillage], pillageHistoryLength], + ); + + const mineQuest = useMemo( + () => ({ + name: QuestName.Mine, + description: "Explore the world, find earthenshard mines", + status: + fragmentMines > 0 + ? questClaimStatus[QuestName.Mine] + ? QuestStatus.Claimed + : QuestStatus.Completed + : QuestStatus.InProgress, + steps: [], + prizes: questPrize.get(QuestName.Mine) || [], + depth: 6, + }), + [questClaimStatus[QuestName.Mine], fragmentMines], + ); + + const contributionQuest = useMemo( + () => ({ + name: QuestName.Contribution, + description: "Contribute to a Hyperstructure", + status: + hyperstructureContributions > 0 + ? questClaimStatus[QuestName.Contribution] + ? QuestStatus.Claimed + : QuestStatus.Completed + : QuestStatus.InProgress, + steps: [], + prizes: questPrize.get(QuestName.Contribution) || [], + depth: 6, + }), + [questClaimStatus[QuestName.Contribution], hyperstructureContributions], + ); + + const hyperstructureQuest = useMemo( + () => ({ + name: QuestName.Hyperstructure, + description: "Build a Hyperstructure", + status: + hyperstructures > 0 + ? questClaimStatus[QuestName.Hyperstructure] + ? QuestStatus.Claimed + : QuestStatus.Completed + : QuestStatus.InProgress, + steps: [], + prizes: questPrize.get(QuestName.Hyperstructure) || [], + depth: 6, + }), + [questClaimStatus[QuestName.Hyperstructure], hyperstructures], + ); + + const quests: Quest[] = useMemo( + () => [ + settleQuest, + buildFarmQuest, + buildResourceQuest, + createTradeQuest, + createArmyQuest, + travelQuest, + buildWorkersHutQuest, + marketQuest, + pillageQuest, + mineQuest, + contributionQuest, + hyperstructureQuest, + ], + [ + settleQuest, + buildFarmQuest, + buildResourceQuest, + createTradeQuest, + createArmyQuest, + travelQuest, + buildWorkersHutQuest, + marketQuest, + pillageQuest, + mineQuest, + contributionQuest, + hyperstructureQuest, + ], + ); + + return { quests }; +}; + +const useQuestDependencies = () => { + const { + setup: { + components: { Contribution, EntityOwner }, + }, + account: { account }, + } = useDojo(); + + const { playerRealms } = useEntities(); + const realm = playerRealms()[0]; + const realmEntityId = realm?.entity_id; + + const entityUpdate = useEntityQuery([HasValue(EntityOwner, { entity_owner_id: BigInt(realmEntityId || "0") })]); + + const buildingQuantities = useBuildingQuantities(realmEntityId); + + const { entityArmies } = useArmiesByEntityOwner({ entity_owner_entity_id: realmEntityId || BigInt("0") }); + const orders = useGetMyOffers(); + + const hasTroops = useMemo(() => armyHasTroops(entityArmies), [entityArmies]); + const hasTraveled = useMemo(() => armyHasTraveled(entityArmies, realm?.position), [entityArmies, realm?.position]); + + const [pillageHistoryLength, setPillageHistoryLength] = useState(0); + + useEffect(() => { + const fetchPillageHistory = async () => { + const eventsLength = await getPillageEvents(BigInt(realmEntityId ?? "0")); + setPillageHistoryLength(eventsLength); + }; + fetchPillageHistory(); + }, [realmEntityId]); + + const { playerStructures } = useEntities(); + const structures = playerStructures(); + + const countStructuresByCategory = useCallback( + (category: string) => { + return structures.filter((structure) => structure.category === category).length; + }, + [structures], + ); + + const fragmentMines = useMemo( + () => countStructuresByCategory(StructureType[StructureType.FragmentMine]), + [realmEntityId], + ); + + const hyperstructures = useMemo( + () => countStructuresByCategory(StructureType[StructureType.Hyperstructure]), + [realmEntityId], + ); + + const hyperstructureContributions = useMemo( + () => runQuery([HasValue(Contribution, { player_address: BigInt(account.address) })]).size, + [realmEntityId], + ); + + return useMemo( + () => ({ + buildingQuantities, + entityArmies, + orders, + pillageHistoryLength, + hasTroops, + hasTraveled, + fragmentMines, + hyperstructures, + hyperstructureContributions, + }), + [entityUpdate], + ); +}; + +export const useQuestClaimStatus = () => { + const { + setup: { + components: { HasClaimedStartingResources, EntityOwner }, + }, + } = useDojo(); + + const { playerRealms } = useEntities(); + const realm = playerRealms()[0]; + const realmEntityId = realm?.entity_id; + + const prizeUpdate = useEntityQuery([ + HasValue(HasClaimedStartingResources, { entity_id: BigInt(realmEntityId || "0") }), + ]); + + const checkPrizesClaimed = (prizes: Prize[]) => { + return prizes.every((prize) => { + const value = getComponentValue( + HasClaimedStartingResources, + getEntityIdFromKeys([BigInt(realmEntityId || "0"), BigInt(prize.id)]), + ); + return value?.claimed; + }); + }; + + const questClaimStatus = useMemo(() => { + return Array.from(questPrize.keys()).reduce( + (acc, questName) => ({ + ...acc, + [questName]: checkPrizesClaimed(questPrize.get(questName) || []), + }), + {} as Record, + ); + }, [prizeUpdate]); + + return { questClaimStatus }; +}; + +export const useUnclaimedQuestsCount = () => { + const { questClaimStatus } = useQuestClaimStatus(); + + const unclaimedQuestsCount = useMemo( + () => Object.values(questClaimStatus).filter((claimed) => !claimed).length, + [questClaimStatus], + ); + + return { unclaimedQuestsCount }; +}; + +const useBuildingQuantities = (realmEntityId: bigint | undefined) => { + const { + setup: { + components: { BuildingQuantityv2, EntityOwner }, + }, + } = useDojo(); + const entityUpdate = useEntityQuery([HasValue(EntityOwner, { entity_owner_id: BigInt(realmEntityId || "0") })]); + const getBuildingQuantity = (buildingType: BuildingType) => + getComponentValue(BuildingQuantityv2, getEntityIdFromKeys([BigInt(realmEntityId || "0"), BigInt(buildingType)])) + ?.value || 0; + + return useMemo( + () => ({ + farms: getBuildingQuantity(BuildingType.Farm), + resource: getBuildingQuantity(BuildingType.Resource), + workersHut: getBuildingQuantity(BuildingType.WorkersHut), + markets: getBuildingQuantity(BuildingType.Market), + }), + [realmEntityId, entityUpdate], + ); +}; + +const armyHasTroops = (entityArmies: ArmyInfo[]) => { + return ( + entityArmies && + entityArmies[0] && + (Number(entityArmies[0].troops.knight_count) != 0 || + Number(entityArmies[0].troops.crossbowman_count) != 0 || + Number(entityArmies[0].troops.paladin_count) != 0) + ); +}; + +const armyHasTraveled = (entityArmies: ArmyInfo[], realmPosition: { x: number; y: number }) => { + if (entityArmies && entityArmies[0] && realmPosition) { + return entityArmies[0].x != realmPosition.x || entityArmies[0].y != realmPosition.y; + } + return false; +}; diff --git a/client/src/hooks/helpers/useStamina.tsx b/client/src/hooks/helpers/useStamina.tsx index 506514fa5..054851df6 100644 --- a/client/src/hooks/helpers/useStamina.tsx +++ b/client/src/hooks/helpers/useStamina.tsx @@ -1,5 +1,5 @@ import { ClientComponents } from "@/dojo/createClientComponents"; -import { ResourcesIds, WORLD_CONFIG_ID } from "@bibliothecadao/eternum"; +import { EternumGlobalConfig, ResourcesIds, WORLD_CONFIG_ID } from "@bibliothecadao/eternum"; import { useEntityQuery } from "@dojoengine/react"; import { Component, Has, HasValue, getComponentValue, runQuery } from "@dojoengine/recs"; import { getEntityIdFromKeys } from "@dojoengine/utils"; @@ -80,11 +80,24 @@ export const useStamina = () => { }); }; + const useArmiesCanMoveCount = (entityArmies: any) => { + if (!entityArmies) return 0; + + return entityArmies.filter((entity: any) => { + const stamina = getStamina({ + travelingEntityId: BigInt(entity.entity_id), + currentArmiesTick, + }); + return stamina?.amount >= EternumGlobalConfig.stamina.travelCost; + }).length; + }; + return { optimisticStaminaUpdate, useStaminaByEntityId, getStamina, getMaxStaminaByEntityId, + useArmiesCanMoveCount, }; }; diff --git a/client/src/hooks/store/useQuestStore.tsx b/client/src/hooks/store/useQuestStore.tsx index fe85dbc6a..fa3ab5eef 100644 --- a/client/src/hooks/store/useQuestStore.tsx +++ b/client/src/hooks/store/useQuestStore.tsx @@ -1,349 +1,12 @@ -import { useDojo } from "@/hooks/context/DojoContext"; -import { ArmyInfo, useArmiesByEntityOwner } from "@/hooks/helpers/useArmies"; -import { useGetMyOffers } from "@/hooks/helpers/useTrade"; -import { BuildingType, QuestType, StructureType } from "@bibliothecadao/eternum"; -import { HasValue, getComponentValue, runQuery } from "@dojoengine/recs"; -import { getEntityIdFromKeys } from "@dojoengine/utils"; -import { useCallback, useEffect, useMemo, useState } from "react"; - -import { getPillageEvents } from "@/dojo/events/pillageEventQueries"; -import { View as LeftView } from "@/ui/modules/navigation/LeftNavigationModule"; -import { useLocation } from "wouter"; import { create } from "zustand"; -import { useEntities } from "../helpers/useEntities"; -import useUIStore from "./useUIStore"; - -export enum QuestName { - Settle = "Settle", - BuildFarm = "Build a Farm", - BuildResource = "Build a Resource Facility", - CreateTrade = "Create a Trade", - CreateArmy = "Create an Army", - Travel = "Travel with your Army", - BuildWorkersHut = "Build a workers hut.", - Market = "Build a market.", - Pillage = "Pillage a structure.", - Mine = "Discover an earthenshard mine.", - Contribution = "Contribute to a hyperstructure.", - Hyperstructure = "Build a hyperstructure.", -} - -export interface Quest { - name: string; - description: string; - steps: Step[]; - completed?: boolean; - claimed?: boolean; - prizes: Prize[]; - depth: number; -} - -interface Step { - description: string; - completed: boolean; -} - -export interface Prize { - id: number; - title: string; -} +import { Quest } from "../helpers/useQuests"; export interface QuestStore { - quests: Quest[] | undefined; - setQuests: (quests: Quest[] | undefined) => void; - selectedQuest: Quest | undefined; - setSelectedQuest: (selectedQuest: Quest | undefined) => void; - claimableQuestsLength: number; - setClaimableQuestsLength: (claimableQuestsLength: number) => void; - showCompletedQuests: boolean; - setShowCompletedQuests: (showCompletedQuests: boolean) => void; + selectedQuest: Quest | null; + setSelectedQuest: (selectedQuest: Quest | null) => void; } -export const useQuestStore = create((set) => { - return { - quests: undefined, - setQuests: (quests: Quest[] | undefined) => set({ quests }), - selectedQuest: undefined, - setSelectedQuest: (selectedQuest: Quest | undefined) => set({ selectedQuest }), - claimableQuestsLength: 0, - setClaimableQuestsLength: (claimableQuestsLength: number) => set({ claimableQuestsLength }), - showCompletedQuests: false, - setShowCompletedQuests: (showCompletedQuests: boolean) => set({ showCompletedQuests }), - }; -}); - -export const useQuests = () => { - const { - setup: { - components: { BuildingQuantityv2, HasClaimedStartingResources, Contribution }, - }, - account: { account }, - } = useDojo(); - - const selectedEntity = useUIStore((state) => state.selectedEntity); - const leftView = useUIStore((state) => state.leftNavigationView); - const previewBuilding = useUIStore((state) => state.previewBuilding); - - const setQuests = useQuestStore((state) => state.setQuests); - const selectedQuest = useQuestStore((state) => state.selectedQuest); - const setSelectedQuest = useQuestStore((state) => state.setSelectedQuest); - const setClaimableQuestsLength = useQuestStore((state) => state.setClaimableQuestsLength); - - const { playerRealms } = useEntities(); - const realm = playerRealms()[0]; - const entityId = realm?.entity_id; - const realmPosition = realm?.position; - - const [location, _] = useLocation(); - const isWorldView = useMemo(() => location === "/map", [location]); - - const getBuildingQuantity = (buildingType: BuildingType) => - getComponentValue(BuildingQuantityv2, getEntityIdFromKeys([BigInt(entityId || "0"), BigInt(buildingType)])) - ?.value || 0; - - const farms = getBuildingQuantity(BuildingType.Farm); - const resource = getBuildingQuantity(BuildingType.Resource); - const workersHut = getBuildingQuantity(BuildingType.WorkersHut); - const markets = getBuildingQuantity(BuildingType.Market); - - const { playerStructures } = useEntities(); - const structures = playerStructures(); - - const countStructuresByCategory = useCallback( - (category: string) => { - return structures.filter((structure) => structure.category === category).length; - }, - [structures], - ); - - const fragmentMines = useMemo( - () => countStructuresByCategory(StructureType[StructureType.FragmentMine]), - [countStructuresByCategory], - ); - const hyperstructures = useMemo( - () => countStructuresByCategory(StructureType[StructureType.Hyperstructure]), - [countStructuresByCategory], - ); - - const hyperstructureContributions = runQuery([ - HasValue(Contribution, { player_address: BigInt(account.address) }), - ]).size; - - const orders = useGetMyOffers(); - - const { entityArmies } = useArmiesByEntityOwner({ entity_owner_entity_id: entityId || BigInt("0") }); - - const [pillageHistoryLength, setPillageHistoryLength] = useState(0); - - const hasTroops = useMemo(() => armyHasTroops(entityArmies), [entityArmies]); - const hasTraveled = useMemo(() => armyHasTraveled(entityArmies, realmPosition), [entityArmies, realmPosition]); - - useEffect(() => { - const fetchPillageHistory = async () => { - const eventsLength = await getPillageEvents(BigInt(entityId ?? "0")); - setPillageHistoryLength(eventsLength); - }; - fetchPillageHistory(); - }, [entityId]); - - const quests: Quest[] = useMemo(() => { - const updatedQuests = [ - { - name: QuestName.Settle, - description: "A gift of food from the gods.", - completed: true, - steps: [{ description: "Settle your first Realm.", completed: true }], - prizes: [{ id: QuestType.Food, title: "Common Resources" }], - depth: 0, - }, - { - name: QuestName.BuildFarm, - description: "Wheat is the lifeblood of your people. Go to the construction menu and build a farm.", - completed: farms > 0, - steps: [ - { description: "Navigate to the construction menu.", completed: leftView === LeftView.ConstructionView }, - { - description: "Select the 'Farm' card", - completed: previewBuilding !== null, - }, - { - description: "Left click on a hex to build it, or right click to cancel.", - completed: farms > 0, - }, - ], - prizes: [ - { id: QuestType.CommonResources, title: "Common Resources" }, - { id: QuestType.UncommonResources, title: "Uncommon Resources" }, - { id: QuestType.RareResources, title: "Rare Resources" }, - { id: QuestType.UniqueResources, title: "Unique Resources" }, - { id: QuestType.LegendaryResources, title: "Legendary Resources" }, - { id: QuestType.MythicResources, title: "Mythic Resources" }, - ], - depth: 1, - }, - { - name: QuestName.BuildResource, - description: "Eternum thrives on resources. Construct resource facilities to harvest them efficiently.", - completed: resource > 0, - steps: [ - { description: "Navigate to the construction menu.", completed: leftView === LeftView.ConstructionView }, - { - description: "Navigate to the 'Resources' tab and select a resource card", - completed: previewBuilding !== null, - }, - { - description: "Left click on a hex to build it, or right click to cancel.", - completed: resource > 0, - }, - ], - prizes: [{ id: QuestType.Trade, title: "Donkeys and Lords" }], - depth: 2, - }, - { - name: QuestName.CreateTrade, - description: "Trading is the lifeblood of Eternum. Create a trade to start your economy.", - completed: orders.length > 0, - steps: [], - prizes: [{ id: QuestType.Military, title: "Claim Starting Army" }], - depth: 3, - }, - { - name: QuestName.CreateArmy, - description: "Conquest is fulfilling. Create an army to conquer your enemies.", - completed: entityArmies.length > 0 && hasTroops, - steps: [ - { description: "Create an army to conquer your enemies.", completed: entityArmies.length > 0 }, - { - description: "Assign troops to your army", - completed: hasTroops, - }, - ], - prizes: [{ id: QuestType.Earthenshard, title: "Claim Earthen Shard" }], - depth: 3, - }, - { - name: QuestName.Travel, - description: "Travel with your army.", - completed: armyHasTraveled(entityArmies, realmPosition), - steps: [ - { description: "Go to world view.", completed: isWorldView }, - { - description: "Right click on your army", - completed: selectedEntity != null || hasTraveled, - }, - { description: "Travel w/ your army.", completed: hasTraveled }, - ], - prizes: [{ id: QuestType.Travel, title: "Travel" }], - depth: 4, - }, - { - name: QuestName.BuildWorkersHut, - description: "Build worker huts to extend your population capacity.", - completed: workersHut > 0, - steps: [], - prizes: [{ id: QuestType.Population, title: "Population" }], - depth: 5, - }, - { - name: QuestName.Market, - description: "Build a market to produce donkeys. Donkeys are a resource used to transport goods.", - completed: markets > 0, - steps: [], - prizes: [{ id: QuestType.Market, title: "Market" }], - depth: 5, - }, - { - name: QuestName.Pillage, - description: "Pillage a realm, hyperstructure or earthenshard mine.", - completed: pillageHistoryLength > 0, - steps: [], - prizes: [{ id: QuestType.Pillage, title: "Pillage" }], - depth: 5, - }, - { - name: QuestName.Mine, - description: "Explore the world, find earthenshard mines.", - completed: fragmentMines > 0, - steps: [], - prizes: [{ id: QuestType.Mine, title: "Mine" }], - depth: 5, - }, - { - name: QuestName.Contribution, - description: "Contribute to a Hyperstructure.", - completed: hyperstructureContributions > 0, - steps: [], - prizes: [{ id: QuestType.Contribution, title: "Contribution" }], - depth: 5, - }, - { - name: QuestName.Hyperstructure, - description: "Build a Hyperstructure.", - completed: hyperstructures > 0, - steps: [], - prizes: [{ id: QuestType.Hyperstructure, title: "Hyperstructure" }], - depth: 5, - }, - ]; - - return updatedQuests.map((quest) => { - const claimed = quest.prizes.every((prize) => { - const value = getComponentValue( - HasClaimedStartingResources, - getEntityIdFromKeys([BigInt(entityId || "0"), BigInt(prize.id)]), - ); - return value?.claimed; - }); - return { ...quest, claimed }; - }); - }, [ - farms, - resource, - orders, - entityArmies, - hasTroops, - hasTraveled, - workersHut, - markets, - fragmentMines, - hyperstructures, - hyperstructureContributions, - pillageHistoryLength, - selectedEntity, - isWorldView, - leftView, - previewBuilding, - ]); - - useEffect(() => { - setQuests(quests); - - const claimableQuestsLenght = quests.filter((quest) => !quest.claimed).length; - setClaimableQuestsLength(claimableQuestsLenght); - }, []); - - useEffect(() => { - setQuests(quests); - setSelectedQuest(quests.find((quest) => quest.name === selectedQuest?.name)); - - const claimableQuestsLenght = quests.filter((quest) => !quest.claimed).length; - setClaimableQuestsLength(claimableQuestsLenght); - }, [quests]); -}; - -const armyHasTroops = (entityArmies: ArmyInfo[]) => { - return ( - entityArmies && - entityArmies[0] && - (Number(entityArmies[0].troops.knight_count) != 0 || - Number(entityArmies[0].troops.crossbowman_count) != 0 || - Number(entityArmies[0].troops.paladin_count) != 0) - ); -}; - -const armyHasTraveled = (entityArmies: ArmyInfo[], realmPosition: { x: number; y: number }) => { - if (entityArmies && entityArmies[0] && realmPosition) { - return entityArmies[0].x != realmPosition.x || entityArmies[0].y != realmPosition.y; - } - return false; -}; +export const useQuestStore = create((set) => ({ + selectedQuest: null, + setSelectedQuest: (selectedQuest: Quest | null) => set({ selectedQuest }), +})); diff --git a/client/src/ui/components/HooksComponent.tsx b/client/src/ui/components/HooksComponent.tsx index 923a963cc..c39c8b400 100644 --- a/client/src/ui/components/HooksComponent.tsx +++ b/client/src/ui/components/HooksComponent.tsx @@ -6,14 +6,12 @@ import { Hexagon } from "@/types"; import { useSetExistingStructures } from "@/hooks/store/_mapStore"; import { useComputePointsLeaderboards } from "@/hooks/store/useLeaderBoardStore"; import { useTravelPath } from "./worldmap/hexagon/useTravelPath"; -import { useQuests } from "@/hooks/store/useQuestStore"; export const HooksComponent = () => { useFetchBlockchainData(); useSetExistingStructures(); useComputePointsLeaderboards(); useTravelPath(); - useQuests(); const setHexData = useUIStore((state) => state.setHexData); diff --git a/client/src/ui/components/construction/SelectPreviewBuilding.tsx b/client/src/ui/components/construction/SelectPreviewBuilding.tsx index fce5fcef5..2dfa0f157 100644 --- a/client/src/ui/components/construction/SelectPreviewBuilding.tsx +++ b/client/src/ui/components/construction/SelectPreviewBuilding.tsx @@ -18,7 +18,7 @@ import { import { ReactComponent as InfoIcon } from "@/assets/icons/common/info.svg"; import { useGetRealm } from "@/hooks/helpers/useRealm"; import { useResourceBalance } from "@/hooks/helpers/useResources"; -import { QuestName, useQuestStore } from "@/hooks/store/useQuestStore"; +import { useQuestStore } from "@/hooks/store/useQuestStore"; import useRealmStore from "@/hooks/store/useRealmStore"; import { usePlayResourceSound } from "@/hooks/useUISound"; import { Headline } from "@/ui/elements/Headline"; @@ -32,21 +32,20 @@ import { BUILDING_COSTS_SCALED } from "@bibliothecadao/eternum"; import React, { useMemo, useState } from "react"; import { BUILDING_IMAGES_PATH } from "@/ui/config"; import { HintSection } from "../hints/HintModal"; +import { QuestName, useQuestClaimStatus } from "@/hooks/helpers/useQuests"; // TODO: THIS IS TERRIBLE CODE, PLEASE REFACTOR export const SelectPreviewBuildingMenu = () => { const setPreviewBuilding = useUIStore((state) => state.setPreviewBuilding); const previewBuilding = useUIStore((state) => state.previewBuilding); - const realmEntityId = useRealmStore((state) => state.realmEntityId); - const quests = useQuestStore((state) => state.quests); const selectedQuest = useQuestStore((state) => state.selectedQuest); const { realm } = useGetRealm(realmEntityId); - const { getBalance } = useResourceBalance(); const { playResourceSound } = usePlayResourceSound(); + const { questClaimStatus } = useQuestClaimStatus(); const buildingTypes = Object.keys(BuildingType).filter( (key) => @@ -80,10 +79,6 @@ export const SelectPreviewBuildingMenu = () => { const [selectedTab, setSelectedTab] = useState(1); - const isQuestClaimedByName = (questName: QuestName) => { - const quest = quests?.find((q) => q.name === questName); - return quest?.claimed ?? false; - }; const tabs = useMemo( () => [ @@ -118,7 +113,7 @@ export const SelectPreviewBuildingMenu = () => { return ( { return ( { return ( { const { entityArmies } = useArmiesByEntityOwner({ entity_owner_entity_id: structure?.entity_id }); @@ -54,7 +55,7 @@ export const EntityArmyList = ({ structure }: any) => { onClick={() => handleCreateArmy(false)} disabled={isLoading} className={clsx({ - "animate-pulse": selectedQuest?.name === QuestName.CreateArmy && !selectedQuest.steps[0].completed, + "animate-pulse": selectedQuest?.name === QuestName.CreateArmy && !entityArmies.length, })} > Create Army @@ -83,11 +84,7 @@ export const EntityArmyList = ({ structure }: any) => { /> )} - questing={ - selectedQuest?.name === QuestName.CreateArmy && - selectedQuest.steps[0].completed && - !selectedQuest.steps[1].completed - } + questing={selectedQuest?.name === QuestName.CreateArmy} /> ); diff --git a/client/src/ui/components/quest/QuestInfo.tsx b/client/src/ui/components/quest/QuestInfo.tsx index ec2a85ec9..bc6e2ca14 100644 --- a/client/src/ui/components/quest/QuestInfo.tsx +++ b/client/src/ui/components/quest/QuestInfo.tsx @@ -1,11 +1,12 @@ import { useDojo } from "@/hooks/context/DojoContext"; import { useRealm } from "@/hooks/helpers/useRealm"; -import { Prize, Quest, useQuestStore } from "@/hooks/store/useQuestStore"; +import { useQuestStore } from "@/hooks/store/useQuestStore"; import Button from "@/ui/elements/Button"; import { useState } from "react"; import { Check, ShieldQuestion } from "lucide-react"; import { multiplyByPrecision } from "@/ui/utils/utils"; import { ResourceCost } from "@/ui/elements/ResourceCost"; +import { Prize, Quest, QuestStatus } from "@/hooks/helpers/useQuests"; export const QuestInfo = ({ quest, entityId }: { quest: Quest; entityId: bigint }) => { const { @@ -42,36 +43,42 @@ export const QuestInfo = ({ quest, entityId }: { quest: Quest; entityId: bigint return ( <> - -
+
{quest.name}
- {quest.completed ? : } + {quest.status !== QuestStatus.InProgress ? : }

{quest.description}

- {quest.steps?.map(({ description, completed }, index) => ( + {quest.steps?.map((step: any, index: any) => (
-
- {description}
- {completed ? : } +
- {step}
))}
- {quest.completed && - (quest.claimed ? ( -
Rewards claimed
- ) : ( + {quest.status !== QuestStatus.Claimed ? ( + quest.status === QuestStatus.Completed && ( - ))} + ) + ) : ( +
Rewards claimed
+ )}
diff --git a/client/src/ui/components/quest/QuestList.tsx b/client/src/ui/components/quest/QuestList.tsx index 6c701377f..b3ee6437b 100644 --- a/client/src/ui/components/quest/QuestList.tsx +++ b/client/src/ui/components/quest/QuestList.tsx @@ -1,16 +1,16 @@ -import { Prize, Quest, useQuestStore } from "@/hooks/store/useQuestStore"; +import { useQuestStore } from "@/hooks/store/useQuestStore"; import { useEffect, useMemo, useState } from "react"; import { areAllQuestsClaimed, groupQuestsByDepth } from "./utils"; import Button from "@/ui/elements/Button"; import { useDojo } from "@/hooks/context/DojoContext"; import { useRealm } from "@/hooks/helpers/useRealm"; import { multiplyByPrecision } from "@/ui/utils/utils"; +import { Prize, Quest, QuestStatus, useQuests } from "@/hooks/helpers/useQuests"; export const QuestList = ({ quests, entityId }: { quests: Quest[]; entityId: bigint | undefined }) => { - const showCompletedQuests = useQuestStore((state) => state.showCompletedQuests); - const setShowCompletedQuests = useQuestStore((state) => state.setShowCompletedQuests); + const [showCompletedQuests, setShowCompletedQuests] = useState(false); const [skipTutorial, setSkipTutorial] = useState(false); - const [maxDepthToShow, setMaxDepthToShow] = useState(1); + const [maxDepthToShow, setMaxDepthToShow] = useState(0); const groupedQuests = useMemo(() => groupQuestsByDepth(quests), [quests]); @@ -22,8 +22,7 @@ export const QuestList = ({ quests, entityId }: { quests: Quest[]; entityId: big return depth + 1; } return max; - // need to start from depth 1 - }, 1); + }, 0); setMaxDepthToShow(newMaxDepth); }, [quests, groupedQuests]); @@ -51,9 +50,11 @@ export const QuestList = ({ quests, entityId }: { quests: Quest[]; entityId: big .sort(([a], [b]) => Number(a) - Number(b)) .map(([depth, depthQuests]) => { if (Number(depth) > maxDepthToShow) return null; - const uncompletedQuests = depthQuests.filter((quest) => !quest.claimed || showCompletedQuests); - if (uncompletedQuests.length === 0) return null; - return ; + const shownQuests = depthQuests.filter( + (quest) => quest.status !== QuestStatus.Claimed || showCompletedQuests, + ); + if (shownQuests.length === 0) return null; + return ; })}
@@ -63,7 +64,9 @@ export const QuestList = ({ quests, entityId }: { quests: Quest[]; entityId: big const QuestDepthGroup = ({ depthQuests }: { depthQuests: Quest[] }) => (
- {depthQuests?.map((quest: Quest) => )} + {depthQuests?.map((quest: Quest) => ( + + ))}
); @@ -73,7 +76,7 @@ const QuestCard = ({ quest }: { quest: Quest }) => { return (
setSelectedQuest(quest)} >
{quest.name}
@@ -90,12 +93,12 @@ const SkipTutorial = ({ entityId }: { entityId: bigint }) => { } = useDojo(); const [isLoading, setIsLoading] = useState(false); - const quests = useQuestStore((state) => state.quests); + const { quests } = useQuests(); const { getQuestResources } = useRealm(); const questResources = getQuestResources(); - const unclaimedQuests = quests?.filter((quest) => !quest.claimed); + const unclaimedQuests = quests?.filter((quest) => quest.status === QuestStatus.InProgress); const resourcesToMint = unclaimedQuests?.flatMap((quest: Quest) => diff --git a/client/src/ui/components/quest/QuestPanel.tsx b/client/src/ui/components/quest/QuestPanel.tsx index f614e5b4e..7be27efff 100644 --- a/client/src/ui/components/quest/QuestPanel.tsx +++ b/client/src/ui/components/quest/QuestPanel.tsx @@ -1,18 +1,22 @@ import { useQuestStore } from "@/hooks/store/useQuestStore"; import { QuestInfo } from "./QuestInfo"; import { QuestList } from "./QuestList"; +import { useQuests } from "@/hooks/helpers/useQuests"; +import { useMemo } from "react"; export const QuestPanel = ({ entityId }: { entityId: bigint | undefined }) => { - const { quests, selectedQuest } = useQuestStore((state) => ({ - quests: state.quests, + const { selectedQuest } = useQuestStore((state) => ({ selectedQuest: state.selectedQuest, })); + const { quests } = useQuests(); + const updatedSelectedQuest = quests.find((quest) => quest.name === selectedQuest?.name); + return selectedQuest ? (
- +
) : ( - + ); }; diff --git a/client/src/ui/components/quest/utils.tsx b/client/src/ui/components/quest/utils.tsx index c85a399ce..4cc4afcdd 100644 --- a/client/src/ui/components/quest/utils.tsx +++ b/client/src/ui/components/quest/utils.tsx @@ -1,4 +1,4 @@ -import { Quest } from "@/hooks/store/useQuestStore"; +import { Quest, QuestStatus } from "@/hooks/helpers/useQuests"; export const groupQuestsByDepth = (quests: Quest[]): Record => { return quests?.reduce((groupedQuests: Record, quest) => { @@ -11,4 +11,4 @@ export const groupQuestsByDepth = (quests: Quest[]): Record => }, {}); }; -export const areAllQuestsClaimed = (quests: Quest[]) => quests.every((quest) => quest.claimed); +export const areAllQuestsClaimed = (quests: Quest[]) => quests.every((quest) => quest.status === QuestStatus.Claimed); diff --git a/client/src/ui/components/structures/construction/StructureConstructionMenu.tsx b/client/src/ui/components/structures/construction/StructureConstructionMenu.tsx index 6cc185800..aaf2251f2 100644 --- a/client/src/ui/components/structures/construction/StructureConstructionMenu.tsx +++ b/client/src/ui/components/structures/construction/StructureConstructionMenu.tsx @@ -20,8 +20,9 @@ import { BUILDING_COSTS_SCALED } from "@bibliothecadao/eternum"; import { useResourceBalance } from "@/hooks/helpers/useResources"; import { Headline } from "@/ui/elements/Headline"; import { StructureCard } from "./StructureCard"; -import { QuestName, useQuestStore } from "@/hooks/store/useQuestStore"; +import { useQuestStore } from "@/hooks/store/useQuestStore"; import clsx from "clsx"; +import { QuestName } from "@/hooks/helpers/useQuests"; const STRUCTURE_IMAGE_PREFIX = "/images/buildings/thumb/"; export const STRUCTURE_IMAGE_PATHS = { diff --git a/client/src/ui/modules/navigation/BottomNavigation.tsx b/client/src/ui/modules/navigation/BottomNavigation.tsx index 369227ebb..12947f023 100644 --- a/client/src/ui/modules/navigation/BottomNavigation.tsx +++ b/client/src/ui/modules/navigation/BottomNavigation.tsx @@ -1,5 +1,5 @@ import { useEntities } from "@/hooks/helpers/useEntities"; -import { QuestName, useQuestStore } from "@/hooks/store/useQuestStore"; +import { useQuestStore } from "@/hooks/store/useQuestStore"; import useRealmStore from "@/hooks/store/useRealmStore"; import useUIStore from "@/hooks/store/useUIStore"; import CircleButton from "@/ui/elements/CircleButton"; @@ -11,6 +11,13 @@ import { useMemo } from "react"; import { useLocation } from "wouter"; import { guilds, leaderboard, quests as questsWindow } from "../../components/navigation/Config"; import { BuildingThumbs } from "./LeftNavigationModule"; +import { + QuestName, + QuestStatus, + useUnclaimedQuestsCount, + useQuests, + useQuestClaimStatus, +} from "@/hooks/helpers/useQuests"; export enum MenuEnum { realm = "realm", @@ -27,22 +34,23 @@ export enum MenuEnum { } export const BottomNavigation = () => { - const [location, setLocation] = useLocation(); + const [location, _] = useLocation(); const { realmEntityId } = useRealmStore(); + const { quests } = useQuests(); + const { unclaimedQuestsCount } = useUnclaimedQuestsCount(); + const { questClaimStatus } = useQuestClaimStatus(); + const togglePopup = useUIStore((state) => state.togglePopup); const isPopupOpen = useUIStore((state) => state.isPopupOpen); + const selectedQuest = useQuestStore((state) => state.selectedQuest); const isWorldView = useMemo(() => location === "/map", [location]); - const quests = useQuestStore((state) => state.quests); - const selectedQuest = useQuestStore((state) => state.selectedQuest); - const claimableQuestsLength = useQuestStore((state) => state.claimableQuestsLength); - const { playerStructures } = useEntities(); const structures = useMemo(() => playerStructures(), [playerStructures]); - const questToClaim = quests?.find((quest) => quest.completed && !quest.claimed); + const questToClaim = quests?.find((quest: any) => quest.status === QuestStatus.Completed); const secondaryNavigation = useMemo(() => { return [ @@ -56,7 +64,7 @@ export const BottomNavigation = () => { active={isPopupOpen(questsWindow)} size="lg" onClick={() => togglePopup(questsWindow)} - notification={isRealmSelected(realmEntityId, structures) ? claimableQuestsLength : undefined} + notification={isRealmSelected(realmEntityId, structures) ? unclaimedQuestsCount : undefined} notificationLocation={"topleft"} disabled={!isRealmSelected(realmEntityId, structures)} /> @@ -79,7 +87,7 @@ export const BottomNavigation = () => { active={isPopupOpen(leaderboard)} size="lg" onClick={() => togglePopup(leaderboard)} - className={clsx({ hidden: !quests?.find((quest) => quest.name === QuestName.Travel)?.claimed })} + className={clsx({ hidden: !questClaimStatus[QuestName.Travel] })} /> ), }, @@ -92,12 +100,14 @@ export const BottomNavigation = () => { active={isPopupOpen(guilds)} size="lg" onClick={() => togglePopup(guilds)} - className={clsx({ hidden: !quests?.find((quest) => quest.name === QuestName.Travel)?.claimed })} + className={clsx({ + hidden: !questClaimStatus[QuestName.Travel], + })} /> ), }, ]; - }, [claimableQuestsLength, selectedQuest, quests]); + }, [unclaimedQuestsCount, selectedQuest, quests, realmEntityId]); const slideUp = { hidden: { y: "100%", transition: { duration: 0.3 } }, diff --git a/client/src/ui/modules/navigation/LeftNavigationModule.tsx b/client/src/ui/modules/navigation/LeftNavigationModule.tsx index 2faaa90ec..defef1b68 100644 --- a/client/src/ui/modules/navigation/LeftNavigationModule.tsx +++ b/client/src/ui/modules/navigation/LeftNavigationModule.tsx @@ -2,7 +2,7 @@ import { useArmiesByEntityOwner } from "@/hooks/helpers/useArmies"; import { useEntities } from "@/hooks/helpers/useEntities"; import { useStamina } from "@/hooks/helpers/useStamina"; import useBlockchainStore from "@/hooks/store/useBlockchainStore"; -import { QuestName, useQuestStore } from "@/hooks/store/useQuestStore"; +import { useQuestStore } from "@/hooks/store/useQuestStore"; import useUIStore from "@/hooks/store/useUIStore"; import { SelectPreviewBuildingMenu } from "@/ui/components/construction/SelectPreviewBuilding"; import { StructureConstructionMenu } from "@/ui/components/structures/construction/StructureConstructionMenu"; @@ -26,6 +26,7 @@ import { Leaderboard } from "../leaderboard/LeaderBoard"; import { Questing } from "../questing/Questing"; import { WorldStructuresMenu } from "../world-structures/WorldStructuresMenu"; import { MenuEnum } from "./BottomNavigation"; +import { QuestName, useQuestClaimStatus } from "@/hooks/helpers/useQuests"; export const BuildingThumbs = { hex: "/images/buildings/thumb/question.png", @@ -56,31 +57,26 @@ export enum View { export const LeftNavigationModule = () => { const [lastView, setLastView] = useState(View.None); - const currentArmiesTick = useBlockchainStore((state) => state.currentArmiesTick); const view = useUIStore((state) => state.leftNavigationView); const setView = useUIStore((state) => state.setLeftNavigationView); const previewBuilding = useUIStore((state) => state.previewBuilding); const isPopupOpen = useUIStore((state) => state.isPopupOpen); const openedPopups = useUIStore((state) => state.openedPopups); - const quests = useQuestStore((state) => state.quests); const selectedQuest = useQuestStore((state) => state.selectedQuest); const { realmEntityId } = useRealmStore(); - const { getStamina } = useStamina(); + const { entityArmies } = useArmiesByEntityOwner({ entity_owner_entity_id: realmEntityId }); + const { useArmiesCanMoveCount } = useStamina(); + const armiesCanMoveCount = useArmiesCanMoveCount(entityArmies); + + const { questClaimStatus } = useQuestClaimStatus(); const [location, _] = useLocation(); const isWorldView = useMemo(() => location === "/map", [location]); - const armiesWithStaminaLeft = entityArmies?.filter((entity) => { - return ( - getStamina({ travelingEntityId: BigInt(entity.entity_id), currentArmiesTick })?.amount >= - EternumGlobalConfig.stamina.travelCost - ); - }); - const isBuildQuest = useMemo(() => { return ( selectedQuest?.name === QuestName.BuildFarm || @@ -99,7 +95,7 @@ export const LeftNavigationModule = () => { name: "entityDetails", button: ( quest.name === QuestName.CreateArmy)?.claimed })} + className={clsx({ hidden: !questClaimStatus[QuestName.CreateArmy] })} image={BuildingThumbs.hex} tooltipLocation="top" label={"Details"} @@ -121,7 +117,7 @@ export const LeftNavigationModule = () => { view != View.ConstructionView && selectedQuest?.name === QuestName.CreateArmy && isPopupOpen(questsPopup), - hidden: !quests?.find((quest) => quest.name === QuestName.BuildResource)?.claimed, + hidden: !questClaimStatus[QuestName.CreateTrade], })} image={BuildingThumbs.military} tooltipLocation="top" @@ -132,7 +128,7 @@ export const LeftNavigationModule = () => { setLastView(View.MilitaryView); setView(View.MilitaryView); }} - notification={armiesWithStaminaLeft.length} + notification={armiesCanMoveCount} notificationLocation="topright" /> ), @@ -162,7 +158,7 @@ export const LeftNavigationModule = () => { button: ( quest.name === QuestName.CreateArmy)?.claimed, + hidden: !questClaimStatus[QuestName.CreateArmy], "animate-pulse": view != View.ConstructionView && selectedQuest?.name === QuestName.Contribution && @@ -197,7 +193,8 @@ export const LeftNavigationModule = () => { item.name === MenuEnum.construction || item.name === MenuEnum.worldStructures, ); - }, [location, view, armiesWithStaminaLeft, quests, openedPopups]); + }, [location, view, openedPopups, selectedQuest, armiesCanMoveCount]); + // }, [location, view, armiesWithStaminaLeft, openedPopups]); if (realmEntityId === undefined) { return null; diff --git a/client/src/ui/modules/navigation/RightNavigationModule.tsx b/client/src/ui/modules/navigation/RightNavigationModule.tsx index 469ad5ece..d91008d43 100644 --- a/client/src/ui/modules/navigation/RightNavigationModule.tsx +++ b/client/src/ui/modules/navigation/RightNavigationModule.tsx @@ -12,7 +12,7 @@ import { useMemo, useState } from "react"; import { BaseContainer } from "../../containers/BaseContainer"; import { useEntities } from "@/hooks/helpers/useEntities"; -import { QuestName, useQuestStore } from "@/hooks/store/useQuestStore"; +import { useQuestStore } from "@/hooks/store/useQuestStore"; import { HintSection } from "@/ui/components/hints/HintModal"; import { Headline } from "@/ui/elements/Headline"; import { HintModalButton } from "@/ui/elements/HintModalButton"; @@ -21,6 +21,7 @@ import { motion } from "framer-motion"; import { ArrowRight } from "lucide-react"; import { quests as questsPopup } from "../../components/navigation/Config"; import { BuildingThumbs } from "./LeftNavigationModule"; +import { QuestName, QuestStatus, useQuestClaimStatus } from "@/hooks/helpers/useQuests"; export enum View { None, @@ -37,14 +38,14 @@ export const RightNavigationModule = () => { const isPopupOpen = useUIStore((state) => state.isPopupOpen); const openedPopups = useUIStore((state) => state.openedPopups); + const selectedQuest = useQuestStore((state) => state.selectedQuest); + const { realmEntityId } = useRealmStore(); + const { questClaimStatus } = useQuestClaimStatus(); const { getEntityInfo } = useEntities(); const realmIsMine = getEntityInfo(realmEntityId).isMine; - const quests = useQuestStore((state) => state.quests); - const selectedQuest = useQuestStore((state) => state.selectedQuest); - const { getAllArrivalsWithResources } = useResources(); const { toggleModal } = useModal(); @@ -72,7 +73,7 @@ export const RightNavigationModule = () => { button: ( quest.name === QuestName.CreateTrade)?.claimed })} + className={clsx({ hidden: !questClaimStatus[QuestName.CreateTrade] })} image={BuildingThumbs.trade} tooltipLocation="top" label={"Resource Arrivals"} @@ -95,8 +96,10 @@ export const RightNavigationModule = () => { disabled={!realmIsMine} className={clsx({ "animate-pulse": - selectedQuest?.name === QuestName.CreateTrade && !selectedQuest.completed && isPopupOpen(questsPopup), - hidden: !quests?.find((quest) => quest.name === QuestName.BuildResource)?.claimed, + selectedQuest?.name === QuestName.CreateTrade && + selectedQuest.status !== QuestStatus.Completed && + isPopupOpen(questsPopup), + hidden: !questClaimStatus[QuestName.BuildResource], })} image={BuildingThumbs.scale} tooltipLocation="top" @@ -110,7 +113,7 @@ export const RightNavigationModule = () => { ), }, ]; - }, [location, view, quests, openedPopups, selectedQuest, getAllArrivalsWithResources]); + }, [location, view, questClaimStatus, openedPopups, selectedQuest, getAllArrivalsWithResources]); const slideRight = { hidden: { x: "100%" }, diff --git a/client/src/ui/modules/navigation/TopMiddleNavigation.tsx b/client/src/ui/modules/navigation/TopMiddleNavigation.tsx index d6a448cbb..eeb125143 100644 --- a/client/src/ui/modules/navigation/TopMiddleNavigation.tsx +++ b/client/src/ui/modules/navigation/TopMiddleNavigation.tsx @@ -19,10 +19,11 @@ import { useMemo } from "react"; import { useLocation } from "wouter"; import useBlockchainStore from "../../../hooks/store/useBlockchainStore"; -import { QuestName, useQuestStore } from "@/hooks/store/useQuestStore"; +import { useQuestStore } from "@/hooks/store/useQuestStore"; import { motion } from "framer-motion"; import { useComponentValue } from "@dojoengine/react"; import clsx from "clsx"; +import { QuestName, QuestStatus } from "@/hooks/helpers/useQuests"; const slideDown = { hidden: { y: "-100%" }, @@ -136,7 +137,7 @@ export const TopMiddleNavigation = () => { className={clsx({ "animate-pulse": (selectedQuest?.name === QuestName.Travel || selectedQuest?.name === QuestName.Hyperstructure) && - !selectedQuest.completed && + selectedQuest.status !== QuestStatus.Completed && isHexView, })} onClick={() => { diff --git a/client/src/ui/modules/world-structures/WorldStructuresMenu.tsx b/client/src/ui/modules/world-structures/WorldStructuresMenu.tsx index fcb678b01..aaefb19e6 100644 --- a/client/src/ui/modules/world-structures/WorldStructuresMenu.tsx +++ b/client/src/ui/modules/world-structures/WorldStructuresMenu.tsx @@ -13,16 +13,17 @@ import { useShardMines } from "@/hooks/helpers/useShardMines"; import { useContributions } from "@/hooks/helpers/useContributions"; import { calculateShares } from "@/hooks/store/useLeaderBoardStore"; -import { QuestName, useQuestStore } from "@/hooks/store/useQuestStore"; +import { useQuestStore } from "@/hooks/store/useQuestStore"; import { HintModalButton } from "@/ui/elements/HintModalButton"; import { HintSection } from "@/ui/components/hints/HintModal"; +import { QuestName } from "@/hooks/helpers/useQuests"; export const WorldStructuresMenu = ({}: any) => { + const selectedQuest = useQuestStore((state) => state.selectedQuest); + const { hyperstructures } = useHyperstructures(); const { shardMines } = useShardMines(); - const selectedQuest = useQuestStore((state) => state.selectedQuest); - const hyperstructureExtraContent = (entityId: any) => { const hyperstructure = hyperstructures.find((hyperstructure) => hyperstructure.entity_id === BigInt(entityId)); if (!hyperstructure) return null;