Skip to content

Commit ae71b3d

Browse files
committed
Add sorter node
1 parent 406302d commit ae71b3d

File tree

8 files changed

+264
-3
lines changed

8 files changed

+264
-3
lines changed

mindy-website/src/buildings.rs

Lines changed: 57 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
11
use std::borrow::Cow;
22

33
use mindy::{
4-
types::LAccess,
5-
vm::{Building, CustomBuildingData, InstructionResult, LValue, LogicVM, instructions},
4+
types::{LAccess, content::Item},
5+
vm::{
6+
Building, Content, CustomBuildingData, InstructionResult, LObject, LValue, LogicVM,
7+
instructions,
8+
},
69
};
710
use widestring::U16String;
811

@@ -51,6 +54,58 @@ impl CustomBuildingData for WebMessageData {
5154
}
5255
}
5356

57+
pub struct WebSorterData {
58+
item: Option<&'static Item>,
59+
on_building_change: js_sys::Function,
60+
}
61+
62+
impl WebSorterData {
63+
pub fn new(on_building_change: js_sys::Function) -> Self {
64+
Self {
65+
item: None,
66+
on_building_change,
67+
}
68+
}
69+
}
70+
71+
impl CustomBuildingData for WebSorterData {
72+
fn control(
73+
&mut self,
74+
building: &Building,
75+
_: &LogicVM,
76+
control: LAccess,
77+
p1: Cow<'_, LValue>,
78+
_: Cow<'_, LValue>,
79+
_: Cow<'_, LValue>,
80+
) -> InstructionResult {
81+
if control == LAccess::Config {
82+
self.item = match p1.obj() {
83+
Some(LObject::Content(Content::Item(item))) => Some(*item),
84+
Some(LObject::Null) => None,
85+
_ => return InstructionResult::Ok,
86+
};
87+
88+
on_building_change(
89+
&self.on_building_change,
90+
building.position,
91+
"sorter",
92+
self.item.map(|v| v.logic_id),
93+
);
94+
}
95+
InstructionResult::Ok
96+
}
97+
98+
fn sensor(&mut self, _: &Building, _: &LogicVM, sensor: LAccess) -> Option<LValue> {
99+
Some(match sensor {
100+
LAccess::Config => match self.item {
101+
Some(item) => Content::Item(item).into(),
102+
None => LValue::NULL,
103+
},
104+
_ => return None,
105+
})
106+
}
107+
}
108+
54109
pub struct WebSwitchData {
55110
enabled: bool,
56111
on_building_change: js_sys::Function,

mindy-website/src/lib.rs

Lines changed: 56 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@ use mindy::{
1111
parser::LogicParser,
1212
types::{LAccess, Object, ProcessorConfig, ProcessorLinkConfig, content},
1313
vm::{
14-
Building, BuildingData, EmbeddedDisplayData, InstructionResult, LVar, LogicVM,
14+
Building, BuildingData, Content, EmbeddedDisplayData, InstructionResult, LValue, LVar,
15+
LogicVM,
1516
buildings::{MESSAGE, SWITCH},
1617
variables::Constants,
1718
},
@@ -154,6 +155,19 @@ impl WebLogicVM {
154155
.map_err(|e| e.to_string())
155156
}
156157

158+
pub fn add_sorter(&mut self, position: u32) -> Result<(), String> {
159+
self.vm
160+
.add_building(
161+
Building::new(
162+
content::blocks::FROM_NAME["sorter"],
163+
unpack_point(position),
164+
WebSorterData::new(self.on_building_change.clone()).into(),
165+
),
166+
&self.globals,
167+
)
168+
.map_err(|e| e.to_string())
169+
}
170+
157171
pub fn add_switch(&mut self, position: u32) -> Result<(), String> {
158172
self.vm
159173
.add_building(
@@ -235,6 +249,47 @@ impl WebLogicVM {
235249
Ok(names)
236250
}
237251

252+
pub fn set_sorter_config(
253+
&mut self,
254+
position: u32,
255+
logic_id: Option<i32>,
256+
) -> Result<(), String> {
257+
let position = unpack_point(position);
258+
259+
let item = match logic_id {
260+
Some(logic_id) => Content::Item(
261+
*content::items::FROM_LOGIC_ID
262+
.get(&logic_id)
263+
.ok_or_else(|| format!("invalid logic id: {logic_id}"))?,
264+
)
265+
.into(),
266+
None => LValue::NULL,
267+
};
268+
269+
let building = self
270+
.vm
271+
.building(position)
272+
.ok_or_else(|| format!("building does not exist: {position}"))?;
273+
274+
let BuildingData::Custom(custom) = &mut *building.data.borrow_mut() else {
275+
return Err(format!(
276+
"expected switch at {position} but got {}",
277+
building.block.name
278+
));
279+
};
280+
281+
let _ = custom.control(
282+
building,
283+
&self.vm,
284+
LAccess::Config,
285+
Cow::Owned(item),
286+
Default::default(),
287+
Default::default(),
288+
);
289+
290+
Ok(())
291+
}
292+
238293
pub fn set_switch_enabled(&mut self, position: u32, value: bool) -> Result<(), String> {
239294
let position = unpack_point(position);
240295
let building = self

mindy-website/www/src/components/AddBuildingMenu.tsx

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -230,6 +230,20 @@ export default function AddBuildingMenu() {
230230
Message
231231
</Menu.Item>
232232

233+
<Menu.Item
234+
onClick={() =>
235+
reactFlow.addNodes(
236+
createBuildingNode({
237+
type: "sorter",
238+
size: 1,
239+
data: {},
240+
}),
241+
)
242+
}
243+
>
244+
Sorter
245+
</Menu.Item>
246+
233247
<Menu.Item
234248
onClick={() =>
235249
reactFlow.addNodes(

mindy-website/www/src/components/LogicVMFlow.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,20 +24,23 @@ import DisplayNode, { type DisplayNodeType } from "./nodes/DisplayNode";
2424
import MemoryNode, { type MemoryNodeType } from "./nodes/MemoryNode";
2525
import MessageNode, { type MessageNodeType } from "./nodes/MessageNode";
2626
import ProcessorNode, { type ProcessorNodeType } from "./nodes/ProcessorNode";
27+
import SorterNode, { type SorterNodeType } from "./nodes/SorterNode";
2728
import SwitchNode, { type SwitchNodeType } from "./nodes/SwitchNode";
2829

2930
export type LogicVMNode =
3031
| DisplayNodeType
3132
| MemoryNodeType
3233
| MessageNodeType
3334
| ProcessorNodeType
35+
| SorterNodeType
3436
| SwitchNodeType;
3537

3638
const nodeTypes = {
3739
display: DisplayNode,
3840
memory: MemoryNode,
3941
message: MessageNode,
4042
processor: ProcessorNode,
43+
sorter: SorterNode,
4144
switch: SwitchNode,
4245
};
4346

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
.chip :global(.mantine-Chip-label) {
2+
width: 100%;
3+
}
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
import { Center, Chip, SimpleGrid } from "@mantine/core";
2+
import type { NodeProps, Node } from "@xyflow/react";
3+
import { useEffect, useReducer } from "react";
4+
5+
import { useLogicVM } from "../../hooks";
6+
import type { BuildingNodeData } from "./BuildingNode";
7+
import BuildingNode from "./BuildingNode";
8+
import classes from "./SorterNode.module.css";
9+
10+
function reducer(
11+
_: string | null,
12+
action: number | string | null,
13+
): string | null {
14+
return action?.toString() ?? null;
15+
}
16+
17+
export type SorterNodeType = Node<BuildingNodeData, "sorter">;
18+
19+
export default function SorterNode(props: NodeProps<SorterNodeType>) {
20+
const {
21+
data: { position },
22+
} = props;
23+
24+
const vm = useLogicVM();
25+
26+
const [logicId, setLogicId] = useReducer(reducer, null);
27+
28+
useEffect(() => {
29+
vm.postMessage({ type: "addSorter", position });
30+
31+
return () => {
32+
vm.postMessage({ type: "removeBuilding", position });
33+
};
34+
}, [vm, position]);
35+
36+
return (
37+
<BuildingNode buildingType="sorter" onUpdate={setLogicId} {...props}>
38+
<Center>
39+
<Chip.Group
40+
multiple={false}
41+
value={logicId}
42+
onChange={(value) => {
43+
setLogicId(value);
44+
vm.postMessage({
45+
type: "setSorterConfig",
46+
position,
47+
logicId: parseInt(value),
48+
});
49+
}}
50+
>
51+
<SimpleGrid cols={4} spacing="xs" verticalSpacing="xs">
52+
{[
53+
"Copper",
54+
"Lead",
55+
"Metaglass",
56+
"Graphite",
57+
"Sand",
58+
"Coal",
59+
"Titanium",
60+
"Thorium",
61+
"Scrap",
62+
"Silicon",
63+
"Plastanium",
64+
"Phase Fabric",
65+
"Surge Alloy",
66+
"Spore Pod",
67+
"Blast Comp.",
68+
"Pyratite",
69+
"Beryllium",
70+
"Tungsten",
71+
"Oxide",
72+
"Carbide",
73+
].map((name, i) => (
74+
<Chip
75+
key={name}
76+
value={i.toString()}
77+
className={classes.chip}
78+
size="xs"
79+
onClick={(e) => {
80+
if (e.currentTarget.value === logicId) {
81+
setLogicId(null);
82+
vm.postMessage({
83+
type: "setSorterConfig",
84+
position,
85+
logicId: null,
86+
});
87+
}
88+
}}
89+
>
90+
{name}
91+
</Chip>
92+
))}
93+
</SimpleGrid>
94+
</Chip.Group>
95+
</Center>
96+
</BuildingNode>
97+
);
98+
}

mindy-website/www/src/workers/vm.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,11 @@ interface AddProcessorRequest {
2929
kind: ProcessorKind;
3030
}
3131

32+
interface AddSorterRequest {
33+
type: "addSorter";
34+
position: number;
35+
}
36+
3237
interface AddSwitchRequest {
3338
type: "addSwitch";
3439
position: number;
@@ -47,6 +52,12 @@ interface SetProcessorCodeRequest {
4752
links: Uint32Array;
4853
}
4954

55+
interface SetSorterConfigRequest {
56+
type: "setSorterConfig";
57+
position: number;
58+
logicId: number | null;
59+
}
60+
5061
interface SetSwitchEnabledRequest {
5162
type: "setSwitchEnabled";
5263
position: number;
@@ -68,9 +79,11 @@ export type VMWorkerRequest =
6879
| AddMemoryRequest
6980
| AddMessageRequest
7081
| AddProcessorRequest
82+
| AddSorterRequest
7183
| AddSwitchRequest
7284
| SetMessageTextRequest
7385
| SetProcessorCodeRequest
86+
| SetSorterConfigRequest
7487
| SetSwitchEnabledRequest
7588
| RemoveBuildingRequest
7689
| SetTargetFPSRequest;
@@ -90,6 +103,7 @@ interface BuildingAddedResponse {
90103
export interface BuildingUpdateMap {
91104
message: string;
92105
processor: { links?: Map<number, string>; error?: string };
106+
sorter: number | null;
93107
switch: boolean;
94108
}
95109

mindy-website/www/src/workers/vm.worker.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,19 @@ void init().then(() => {
8888
break;
8989
}
9090

91+
case "addSorter": {
92+
const { position } = request;
93+
94+
vm.add_sorter(position);
95+
postMessage({
96+
type: "buildingAdded",
97+
position,
98+
name: vm.building_name(position),
99+
});
100+
101+
break;
102+
}
103+
91104
case "addSwitch": {
92105
const { position } = request;
93106

@@ -130,6 +143,12 @@ void init().then(() => {
130143
break;
131144
}
132145

146+
case "setSorterConfig": {
147+
const { position, logicId } = request;
148+
vm.set_sorter_config(position, logicId);
149+
break;
150+
}
151+
133152
case "setSwitchEnabled": {
134153
const { position, value } = request;
135154
vm.set_switch_enabled(position, value);

0 commit comments

Comments
 (0)