Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 12 additions & 10 deletions src/components/Factory/Canvas/Edges/ResourceEdge.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -37,12 +37,14 @@ const ResourceEdge = ({
clearContent,
setOpen: setContextPanelOpen,
} = useContextPanel();
const { getLatestBuildingStats } = useStatistics();

const sourceStats = getLatestBuildingStats(source);
const resourcesTransferred = sourceStats?.stockpileChanges.filter(
(c) => c.removed > 0,
);
const { getLatestEdgeStats } = useStatistics();

const edgeResource = isResourceData(data) ? data.type : undefined;
const edgeTransfers =
edgeResource === "any"
? getLatestEdgeStats(id, true) // todo: saved stats appear to be incorrect after merging and splitting again (especially if it's a sushi belt) - note the correct amounts are transferred downstream to the building. it's just the value saved int he stats & shown onscreen that is wrong.
: getLatestEdgeStats(id);

const [edgePath, labelX, labelY] = getBezierPath({
sourceX,
Expand All @@ -68,6 +70,7 @@ const ResourceEdge = ({
resource={data}
sourceNodeId={source}
targetNodeId={target}
edgeId={id}
/>,
);
setContextPanelOpen(true);
Expand All @@ -83,6 +86,7 @@ const ResourceEdge = ({
data,
source,
target,
id,
setContent,
clearContent,
setContextPanelOpen,
Expand Down Expand Up @@ -111,19 +115,17 @@ const ResourceEdge = ({
}}
interactionWidth={20}
/>
{resourcesTransferred && (
{edgeTransfers && edgeTransfers.length > 0 && (
<EdgeLabelRenderer>
<div
style={{
position: "absolute",
transform: `translate(-50%, -50%) translate(${labelX}px,${labelY}px) rotate(${labelRotation}deg) translate(0, -10px)`,
transform: `translate(-50%, -50%) translate(${labelX}px, ${labelY}px) rotate(${labelRotation}deg) translateY(-8px)`,
pointerEvents: "all",
}}
className="nodrag nopan"
>
<TransportationFeedback
resourcesTransferred={resourcesTransferred}
/>
<TransportationFeedback transfers={edgeTransfers} />
</div>
</EdgeLabelRenderer>
)}
Expand Down
33 changes: 26 additions & 7 deletions src/components/Factory/Canvas/Nodes/Building.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,11 @@ import {
} from "@xyflow/react";
import { useEffect, useRef } from "react";

import { BlockStack, InlineStack } from "@/components/ui/layout";
import { cn } from "@/lib/utils";
import { useContextPanel } from "@/providers/ContextPanelProvider";

import BuildingIcon from "../../components/BuildingIcon";
import { ProductionFeedback } from "../../components/ProductionFeedback";
import BuildingContext from "../../Context/Building/BuildingContext";
import { isGlobalResource } from "../../data/resources";
Expand Down Expand Up @@ -80,6 +82,7 @@ const Building = ({ id, data, selected }: NodeProps) => {
}

const {
type,
icon,
name,
description,
Expand All @@ -96,6 +99,8 @@ const Building = ({ id, data, selected }: NodeProps) => {
const inputIndexAtPosition: Record<string, number> = {};
const outputIndexAtPosition: Record<string, number> = {};

const isSplitterOrMerger = type === "splitter" || type === "merger";

return (
<div
className={cn("bg-white rounded-lg", selected && "ring-2 ring-selected")}
Expand Down Expand Up @@ -130,15 +135,29 @@ const Building = ({ id, data, selected }: NodeProps) => {
})}

<div
className="px-6 py-4 shadow-lg rounded-lg border-4"
className="p-4 shadow-lg rounded-lg border-4"
style={{ borderColor: color, backgroundColor: `${color}20` }}
>
<div className="font-bold text-lg" style={{ color }}>
{icon} {name}
</div>
<div className="text-sm mt-1 text-center" style={{ color }}>
{description}
</div>
{isSplitterOrMerger ? (
<div
className="font-bold text-xl w-4 h-4 flex justify-center items-center"
style={{ color }}
>
<BuildingIcon icon={icon} />
</div>
) : (
<BlockStack gap="1" align="center">
<InlineStack gap="1" align="center" blockAlign="center">
<BuildingIcon icon={icon} />
<p className="font-bold text-lg" style={{ color }}>
{name}
</p>
</InlineStack>
<div className="text-sm mt-1 text-center" style={{ color }}>
{description}
</div>
</BlockStack>
)}
</div>

{outputs.map((output, globalIndex) => {
Expand Down
2 changes: 2 additions & 0 deletions src/components/Factory/Canvas/callbacks/onConnect.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ export const createOnConnect = (
connection.target,
edgeResource,
reactFlowInstance,
connection.sourceHandle,
connection.targetHandle,
);

if (!newEdge) return;
Expand Down
5 changes: 3 additions & 2 deletions src/components/Factory/Context/Building/BuildingContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@ import { useReactFlow, useUpdateNodeInternals } from "@xyflow/react";
import { BlockStack } from "@/components/ui/layout";
import { Separator } from "@/components/ui/separator";

import { getBuildingDefinition } from "../../data/buildings";
import { configureBuildingInstanceForMethod } from "../../objects/production/configureBuildingInstanceForMethod";
import { type BuildingInstance, getBuildingType } from "../../types/buildings";
import { type BuildingInstance } from "../../types/buildings";
import type { ProductionMethod } from "../../types/production";
import { ContextHeader } from "../shared/ContextHeader";
import { BuildingDescription } from "./BuildingDescription";
Expand All @@ -21,7 +22,7 @@ const BuildingContext = ({ building, nodeId }: BuildingContextProps) => {
const { updateNodeData, getEdges, setEdges } = useReactFlow();
const updateNodeInternals = useUpdateNodeInternals();

const buildingClass = getBuildingType(building.type);
const buildingClass = getBuildingDefinition(building.type);
const availableMethods = buildingClass.productionMethods;

const {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { SPECIAL_BUILDINGS } from "@/components/Factory/data/buildings";
import {
getResourceTypeFoodValue,
isGlobalResource,
RESOURCES,
} from "@/components/Factory/data/resources";
import type { BuildingType } from "@/components/Factory/types/buildings";
Expand All @@ -23,12 +24,8 @@ export const ProductionInputs = ({

// Determine which global resource this special building outputs
const globalOutput = isSpecialBuilding
? productionMethod.outputs.find(
(o) =>
o.resource === "money" ||
o.resource === "food" ||
o.resource === "knowledge",
)?.resource
? productionMethod.outputs.find((o) => isGlobalResource(o.resource))
?.resource
: null;

return (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,14 @@ export const ProductionOutputs = ({
Outputs:
</Text>
{productionMethod.outputs.map((output, idx) => {
if (output.resource === "any") {
return (
<InlineStack key={idx} gap="2">
<Text size="sm">• any</Text>
</InlineStack>
);
}

if (isGlobalResource(output.resource)) {
return (
<InlineStack key={idx} gap="2">
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import type { BuildingType } from "@/components/Factory/types/buildings";
import { Icon } from "@/components/ui/icon";
import { BlockStack, InlineStack } from "@/components/ui/layout";
import { Text } from "@/components/ui/typography";
Expand All @@ -13,7 +14,7 @@ import { ProductionMethodSwitcher } from "./ProductionMethodSwitcher";
import { ProductionProgress } from "./Progress";

interface ProductionMethodSectionProps {
buildingType: string;
buildingType: BuildingType;
productionMethod?: ProductionMethod;
productionState?: ProductionState;
availableMethods?: ProductionMethod[];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,14 +76,6 @@ export const StockpileSection = ({
{resource}: {amount}
</Text>
<StockpileChangeIndicator change={change} />
<div className="flex-1 h-2 bg-gray-200 rounded-full overflow-hidden">
<div
className="h-full bg-blue-500 transition-all"
style={{
width: `${(stock.amount / stock.maxAmount) * 100}%`,
}}
/>
</div>
</InlineStack>
);
},
Expand Down
21 changes: 11 additions & 10 deletions src/components/Factory/Context/ResourceContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,22 +10,22 @@ interface ResourceContextProps {
resource: Resource;
sourceNodeId?: string;
targetNodeId?: string;
edgeId?: string;
}

const ResourceContext = ({
resource,
sourceNodeId,
targetNodeId,
edgeId,
}: ResourceContextProps) => {
const { name, description, color, icon, value, foodValue } = resource;

const { getLatestBuildingStats } = useStatistics();
const sourceStats = sourceNodeId
? getLatestBuildingStats(sourceNodeId)
: undefined;
const resourcesTransferred = sourceStats?.stockpileChanges.filter(
(c) => c.removed > 0,
);
const { getLatestEdgeStats } = useStatistics();

const edgeTransfers = edgeId
? getLatestEdgeStats(edgeId, resource.type === "any")
: [];

return (
<BlockStack
Expand Down Expand Up @@ -77,10 +77,11 @@ const ResourceContext = ({
<Text size="xs" weight="light">
Transported Yesterday:
</Text>
{resourcesTransferred && resourcesTransferred.length > 0 ? (
resourcesTransferred.map((c, index) => (
{edgeTransfers.length > 0 ? (
edgeTransfers.map((transfer, index) => (
<Text key={index} size="xs" tone="subdued">
{RESOURCES[c.resource].icon} {c.removed} {name}
{RESOURCES[transfer.resource].icon} {transfer.transferred}{" "}
{RESOURCES[transfer.resource].name}
</Text>
))
) : (
Expand Down
40 changes: 17 additions & 23 deletions src/components/Factory/Sidebar/BuildingFolder.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,29 +5,17 @@ import {
CollapsibleContent,
CollapsibleTrigger,
} from "@/components/ui/collapsible";
import { Icon, type IconName } from "@/components/ui/icon";
import { Icon } from "@/components/ui/icon";
import { BlockStack, InlineStack } from "@/components/ui/layout";
import { cn } from "@/lib/utils";

import type { BuildingCategory, BuildingType } from "../types/buildings";
import {
BUILDING_CATEGORIES,
type BuildingCategory,
type BuildingType,
} from "../types/buildings";
import BuildingItem from "./BuildingItem";

const CATEGORY_LABELS: Record<BuildingCategory, string> = {
special: "Special",
production: "Production",
refining: "Refining",
utility: "Utility",
storage: "Storage",
};

const CATEGORY_ICONS: Record<BuildingCategory, IconName> = {
special: "Star",
production: "Hammer",
refining: "Factory",
utility: "Wrench",
storage: "Package",
};

type BuildingFolderProps = {
category: BuildingCategory;
buildings: BuildingType[];
Expand All @@ -36,8 +24,14 @@ type BuildingFolderProps = {
const BuildingFolder = ({ category, buildings }: BuildingFolderProps) => {
const [isOpen, setIsOpen] = useState(false);

const buildingCategory = BUILDING_CATEGORIES.find(
(cat) => cat.type === category,
);

if (!buildingCategory) return null;

return (
<Collapsible open={isOpen} onOpenChange={setIsOpen}>
<Collapsible open={isOpen} onOpenChange={setIsOpen} className="w-9/10">
<CollapsibleTrigger
className={cn(
"flex items-center justify-between w-full px-2 py-1.5 rounded-sm",
Expand All @@ -46,12 +40,12 @@ const BuildingFolder = ({ category, buildings }: BuildingFolderProps) => {
>
<InlineStack gap="2" align="center">
<Icon
name={CATEGORY_ICONS[category]}
name={buildingCategory.icon}
size="sm"
className="text-gray-600"
/>
<span className="text-sm font-semibold text-gray-700">
{CATEGORY_LABELS[category]}
{buildingCategory.label}
</span>
<span className="text-xs text-gray-500">({buildings.length})</span>
</InlineStack>
Expand All @@ -65,8 +59,8 @@ const BuildingFolder = ({ category, buildings }: BuildingFolderProps) => {
/>
</CollapsibleTrigger>

<CollapsibleContent>
<BlockStack gap="1" className="mt-1 ml-2">
<CollapsibleContent className="w-full">
<BlockStack gap="1" className="mt-1 ml-2 w-full">
{buildings.map((buildingType) => (
<BuildingItem key={buildingType} buildingType={buildingType} />
))}
Expand Down
8 changes: 5 additions & 3 deletions src/components/Factory/Sidebar/BuildingItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,17 @@ import type { DragEvent } from "react";
import { InlineStack } from "@/components/ui/layout";
import { cn } from "@/lib/utils";

import BuildingIcon from "../components/BuildingIcon";
import { getBuildingDefinition } from "../data/buildings";
import { RESOURCES } from "../data/resources";
import { type BuildingType, getBuildingType } from "../types/buildings";
import type { BuildingType } from "../types/buildings";

interface BuildingItemProps {
buildingType: BuildingType;
}

const BuildingItem = ({ buildingType }: BuildingItemProps) => {
const building = getBuildingType(buildingType);
const building = getBuildingDefinition(buildingType);

const onDragStart = (event: DragEvent) => {
event.dataTransfer.setData(
Expand Down Expand Up @@ -40,7 +42,7 @@ const BuildingItem = ({ buildingType }: BuildingItemProps) => {
onDragStart={onDragStart}
>
<InlineStack wrap="nowrap" gap="2" className="w-full">
<span className="text-xl shrink-0">{building.icon}</span>
<BuildingIcon icon={building.icon} />
<div className="flex flex-col flex-1 min-w-0">
<span
className="truncate text-xs text-gray-800 font-medium"
Expand Down
Loading
Loading