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
71 changes: 57 additions & 14 deletions src/components/Factory/Canvas/Edges/ResourceEdge.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,19 @@
import type { EdgeProps } from "@xyflow/react";
import { BaseEdge, getBezierPath, useEdges } from "@xyflow/react";
import {
BaseEdge,
EdgeLabelRenderer,
getBezierPath,
useEdges,
} from "@xyflow/react";
import { useEffect } from "react";

import { useContextPanel } from "@/providers/ContextPanelProvider";

import { TransportationFeedback } from "../../components/TransportationFeedback";
import ResourceContext from "../../Context/ResourceContext";
import { useStatistics } from "../../providers/StatisticProvider";
import { isResourceData } from "../../types/resources";
import { getBezierMidpointAngle } from "../../utils/bezier";

const ResourceEdge = ({
id,
Expand All @@ -29,8 +37,14 @@ const ResourceEdge = ({
clearContent,
setOpen: setContextPanelOpen,
} = useContextPanel();
const { getLatestBuildingStats } = useStatistics();

const [edgePath] = getBezierPath({
const sourceStats = getLatestBuildingStats(source);
const resourcesTransferred = sourceStats?.stockpileChanges.filter(
(c) => c.removed > 0,
);

const [edgePath, labelX, labelY] = getBezierPath({
sourceX,
sourceY,
sourcePosition,
Expand Down Expand Up @@ -74,19 +88,48 @@ const ResourceEdge = ({
setContextPanelOpen,
]);

const labelRotation = getBezierMidpointAngle(
sourceX,
sourceY,
targetX,
targetY,
sourcePosition,
targetPosition,
);

return (
<BaseEdge
id={id}
path={edgePath}
markerEnd={markerEnd}
style={{
stroke: edgeColor,
strokeWidth: 3,
filter: "drop-shadow(0 0 1px black)",
...style,
}}
interactionWidth={20}
/>
<>
<BaseEdge
id={id}
path={edgePath}
markerEnd={markerEnd}
style={{
stroke: edgeColor,
strokeWidth: 3,
filter: "drop-shadow(0 0 1px black)",
...style,
}}
interactionWidth={20}
/>
{resourcesTransferred && (
<EdgeLabelRenderer>
<div
style={{
position: "absolute",
transform: `translate(-50%, -50%) translate(${labelX}px,${labelY}px) rotate(${labelRotation}deg)`,
pointerEvents: "all",
}}
className="nodrag nopan"
>
<div className="absolute bottom-1">
<TransportationFeedback
resourcesTransferred={resourcesTransferred}
/>
</div>
</div>
</EdgeLabelRenderer>
)}
</>
);
};

Expand Down
13 changes: 11 additions & 2 deletions src/components/Factory/Canvas/callbacks/onConnect.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import type { Connection, Edge } from "@xyflow/react";

import { RESOURCES } from "../../data/resources";
import type { ResourceType } from "../../types/resources";
import { extractResource } from "../../utils/string";

export const createOnConnect = (
Expand All @@ -20,7 +21,15 @@ export const createOnConnect = (
return;
}

const edgeResource = sourceResource ?? targetResource;
let edgeResource: ResourceType | null = null;

if (sourceResource === "any" && targetResource !== "any") {
edgeResource = targetResource;
} else if (targetResource === "any" && sourceResource !== "any") {
edgeResource = sourceResource;
} else if (sourceResource === targetResource) {
edgeResource = sourceResource;
}

if (!edgeResource) {
console.error("Invalid resource type:", edgeResource);
Expand All @@ -31,7 +40,7 @@ export const createOnConnect = (
...connection,
id: `${connection.source}-${connection.sourceHandle}-${connection.target}-${connection.targetHandle}`,
type: "resourceEdge",
data: { ...RESOURCES[edgeResource] },
data: { ...RESOURCES[edgeResource], type: edgeResource },
animated: true,
};

Expand Down
29 changes: 27 additions & 2 deletions src/components/Factory/Context/ResourceContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import { BlockStack, InlineStack } from "@/components/ui/layout";
import { Separator } from "@/components/ui/separator";
import { Text } from "@/components/ui/typography";

import { RESOURCES } from "../data/resources";
import { useStatistics } from "../providers/StatisticProvider";
import type { Resource } from "../types/resources";

interface ResourceContextProps {
Expand All @@ -15,7 +17,15 @@ const ResourceContext = ({
sourceNodeId,
targetNodeId,
}: ResourceContextProps) => {
const { name, description, color, icon, value } = resource;
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,
);

return (
<BlockStack
Expand All @@ -38,11 +48,12 @@ const ResourceContext = ({
{description}
</Text>

<InlineStack gap="2" align="center">
<InlineStack gap="2" blockAlign="center">
<Text size="sm" weight="semibold">
Value:
</Text>
<Text size="sm">💰 {value}</Text>
{foodValue && <Text size="sm">/ 🍎 {foodValue}</Text>}
</InlineStack>

<Separator />
Expand All @@ -63,6 +74,20 @@ const ResourceContext = ({
</Text>
)}
</BlockStack>
<Text size="xs" weight="light">
Transported Yesterday:
</Text>
{resourcesTransferred && resourcesTransferred.length > 0 ? (
resourcesTransferred.map((c, index) => (
<Text key={index} size="xs" tone="subdued">
{RESOURCES[c.resource].icon} {c.removed} {name}
</Text>
))
) : (
<Text size="xs" tone="subdued">
No recent transfers
</Text>
)}
</BlockStack>
</BlockStack>
);
Expand Down
28 changes: 28 additions & 0 deletions src/components/Factory/components/TransportationFeedback.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { InlineStack } from "@/components/ui/layout";

import { RESOURCES } from "../data/resources";
import type { StockpileChange } from "../types/statistics";

interface TransportationFeedbackProps {
resourcesTransferred?: StockpileChange[];
}

export const TransportationFeedback = ({
resourcesTransferred,
}: TransportationFeedbackProps) => {
if (!resourcesTransferred || resourcesTransferred.length === 0) {
return null;
}

return (
<InlineStack wrap="nowrap" gap="1">
{resourcesTransferred.map((c, index) => (
<p
key={index}
className="text-[8px] font-light whitespace-nowrap opacity-60"
style={{ color: RESOURCES[c.resource].color }}
>{`${RESOURCES[c.resource].icon} ${c.removed}`}</p>
))}
</InlineStack>
);
};
29 changes: 28 additions & 1 deletion src/components/Factory/data/buildings.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
import type { BuildingClass } from "../types/buildings";

export const SPECIAL_BUILDINGS = ["firepit", "marketplace", "granary"]; // Buildings with special processing logic that doesn't fit the standard production model
export const SPECIAL_BUILDINGS = [
"firepit",
"marketplace",
"storagepit",
"granary",
]; // Buildings with special processing logic that doesn't fit the standard production model

export const BUILDINGS: Record<string, BuildingClass> = {
firepit: {
Expand Down Expand Up @@ -64,6 +69,28 @@ export const BUILDINGS: Record<string, BuildingClass> = {
},
],
},
storagepit: {
name: "Storage Pit",
icon: "📦",
description: "Stores anything!",
cost: 0,
color: "#A9A9A9",
category: "storage",
productionMethods: [
{
name: "Fill",
inputs: [{ resource: "any", amount: 100, nodes: 4 }],
outputs: [],
days: 1,
},
{
name: "Empty",
inputs: [],
outputs: [{ resource: "any", amount: 100, nodes: 4 }],
days: 1,
},
],
},
well: {
name: "Well",
icon: "💧",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -198,4 +198,51 @@ export const processSpecialBuilding = (
buildingInstance: updatedBuilding,
};
}

// Storage Pit:
// Production Method "Fill" - it only stockpiles input resources
// Production Method "Empty" - it only outputs resources from its stockpile
// Actual transfer of resources is done prior to this step, so here we just manage the production state
if (building.type === "storagepit") {
const storingMethod = building.productionMethod.name === "Fill";
const retrievingMethod = building.productionMethod.name === "Empty";

const storagePitStats = buildingStats.get(node.id)!;

if (!building.stockpile || building.stockpile.length === 0) {
node.data = {
...node.data,
buildingInstance: {
...building,
productionState: { progress: 0, status: "idle" },
},
};
return;
}

if (storingMethod) {
node.data = {
...node.data,
buildingInstance: {
...building,
productionState: { progress: 0, status: "idle" },
},
};
} else if (retrievingMethod) {
const hasTransferredResources = storagePitStats.stockpileChanges.some(
(c) => c.removed > 0,
);

node.data = {
...node.data,
buildingInstance: {
...building,
productionState: {
progress: hasTransferredResources ? 1 : 0,
status: hasTransferredResources ? "complete" : "idle",
},
},
};
}
}
};
Loading
Loading