Skip to content

Commit 7bd3997

Browse files
committed
Added stack and move region operator in editor mode
1 parent 49439de commit 7bd3997

2 files changed

Lines changed: 122 additions & 22 deletions

File tree

src/editor/modules/region_operations.ts

Lines changed: 103 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -7,58 +7,127 @@ import { Server } from "@notbeer-api";
77
import { PatternUIBuilder } from "editor/pane/pattern";
88
import { MaskUIBuilder } from "editor/pane/mask";
99
import { Mask } from "@modules/mask";
10+
import { Cardinal } from "@modules/directions";
11+
12+
enum RegionOperatorMode {
13+
Fill,
14+
Outline,
15+
Wall,
16+
Stack,
17+
Move,
18+
}
19+
20+
const directions = Object.keys(Cardinal.Dir);
21+
// Object.keys on an enum returns not just the enumerators, but there number equivalent.
22+
// Removing those with splice()
23+
directions.splice(0, directions.length / 2);
1024

1125
export class RegionOpModule extends EditorModule {
1226
private pane: UIPane;
27+
private readonly patternUIBuilder = new PatternUIBuilder(new Pattern("stone"));
28+
private readonly maskUIBuilder = new MaskUIBuilder(new Mask("air"));
29+
private enableMask = false;
30+
private direction = Cardinal.Dir.FORWARD;
31+
private distance = 5;
32+
private stackCount = 1;
33+
private mode = 0;
1334

1435
constructor(session: IPlayerUISession) {
1536
super(session);
1637
const tool = session.toolRail.addTool("worldedit:region_operations", { title: "WorldEdit Region Operations" });
17-
const patternUIBuilder = new PatternUIBuilder(new Pattern("stone"));
18-
const maskUIBuilder = new MaskUIBuilder(new Mask("air"));
19-
let enableMask = false;
20-
let mode = 0;
2138

2239
this.pane = new UIPane(this.session, {
2340
title: "Region Operations",
2441
items: [
2542
{
2643
type: "dropdown",
2744
title: "Mode",
28-
value: mode,
45+
value: this.mode,
2946
entries: [
30-
{ label: "Fill Selection", value: 0 },
31-
{ label: "Outline Selection", value: 1 },
32-
{ label: "Wall Selection", value: 2 },
47+
{ label: "Fill Selection", value: RegionOperatorMode.Fill },
48+
{ label: "Outline Selection", value: RegionOperatorMode.Outline },
49+
{ label: "Wall Selection", value: RegionOperatorMode.Wall },
50+
{ label: "Stack Selection", value: RegionOperatorMode.Stack },
51+
{ label: "Move Selection", value: RegionOperatorMode.Move },
3352
],
3453
onChange: (value) => {
35-
mode = value;
36-
const usePatternAndMask = mode >= 0 && mode <= 2;
37-
this.pane.getSubPane("pattern").visible = usePatternAndMask;
38-
this.pane.getSubPane("mask").visible = usePatternAndMask;
54+
this.mode = value;
55+
this.updatePane();
3956
},
4057
},
4158
{
4259
type: "button",
4360
title: "Execute Operation",
4461
enable: this.canOperate(),
4562
pressed: () => {
46-
const mode = this.pane.getValue(0) as number;
47-
if (mode >= 0 && mode <= 2) {
63+
if (this.usesPatternAndMask()) {
4864
const args = new Map<string, any>([
49-
["pattern", patternUIBuilder.pattern],
50-
["mask", enableMask ? maskUIBuilder.mask : new Mask()],
65+
["pattern", this.patternUIBuilder.pattern],
66+
["mask", this.enableMask ? this.maskUIBuilder.mask : new Mask()],
5167
]);
52-
const command = mode === 0 ? "replace" : mode === 1 ? "faces" : mode === 2 ? "walls" : "";
68+
const command = {
69+
[RegionOperatorMode.Fill]: "replace",
70+
[RegionOperatorMode.Outline]: "faces",
71+
[RegionOperatorMode.Wall]: "walls",
72+
}[this.mode];
5373
Server.command.getRegistration(command).callback(this.player, "editor-callback", args);
74+
} else if (this.mode === RegionOperatorMode.Stack) {
75+
Server.command.getRegistration("stack").callback(
76+
this.player,
77+
"editor-callback",
78+
new Map(
79+
Object.entries({
80+
a: true,
81+
count: this.stackCount,
82+
offset: new Cardinal(this.direction),
83+
})
84+
)
85+
);
86+
} else if (this.mode === RegionOperatorMode.Move) {
87+
Server.command.getRegistration("move").callback(
88+
this.player,
89+
"editor-callback",
90+
new Map(
91+
Object.entries({
92+
a: true,
93+
amount: this.distance,
94+
offset: new Cardinal(this.direction),
95+
})
96+
)
97+
);
5498
}
5599
},
56100
},
101+
{ type: "divider" },
102+
{
103+
type: "dropdown",
104+
title: "Direction",
105+
uniqueId: "direction",
106+
entries: directions.map((dir, index) => ({ label: dir, value: index })),
107+
value: Cardinal.Dir.FORWARD,
108+
onChange: (value) => (this.direction = value),
109+
},
110+
{
111+
type: "slider",
112+
title: "Distance",
113+
uniqueId: "distance",
114+
min: 1,
115+
value: 5,
116+
onChange: (value) => (this.distance = value),
117+
},
118+
{
119+
type: "slider",
120+
title: "Stack Count",
121+
uniqueId: "stackCount",
122+
min: 1,
123+
value: 1,
124+
onChange: (value) => (this.stackCount = value),
125+
},
57126
{
58127
type: "subpane",
59128
title: "Pattern",
60129
uniqueId: "pattern",
61-
items: patternUIBuilder,
130+
items: this.patternUIBuilder,
62131
},
63132
{
64133
type: "subpane",
@@ -68,11 +137,11 @@ export class RegionOpModule extends EditorModule {
68137
{
69138
type: "toggle",
70139
title: "Enable Mask",
71-
value: enableMask,
140+
value: this.enableMask,
72141
onChange: (value) => {
73-
enableMask = value;
142+
this.enableMask = value;
74143
const pane = this.pane.getSubPane("mask").getSubPane(1);
75-
if (value) maskUIBuilder.build(pane);
144+
if (value) this.maskUIBuilder.build(pane);
76145
else pane.changeItems([]);
77146
},
78147
},
@@ -88,6 +157,19 @@ export class RegionOpModule extends EditorModule {
88157
});
89158
this.pane.bindToTool(tool);
90159
this.session.extensionContext.afterEvents.SelectionChange.subscribe(this.onSelectionChange);
160+
this.updatePane();
161+
}
162+
163+
private updatePane() {
164+
this.pane.getSubPane("pattern").visible = this.usesPatternAndMask();
165+
this.pane.getSubPane("mask").visible = this.usesPatternAndMask();
166+
this.pane.setVisibility("direction", this.mode === RegionOperatorMode.Stack || this.mode === RegionOperatorMode.Move);
167+
this.pane.setVisibility("distance", this.mode === RegionOperatorMode.Move);
168+
this.pane.setVisibility("stackCount", this.mode === RegionOperatorMode.Stack);
169+
}
170+
171+
private usesPatternAndMask() {
172+
return this.mode === RegionOperatorMode.Fill || this.mode === RegionOperatorMode.Outline || this.mode === RegionOperatorMode.Wall;
91173
}
92174

93175
private canOperate() {

src/editor/pane/builder.ts

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,10 @@ interface BasePaneItem {
2828
uniqueId?: string;
2929
}
3030

31+
interface DividerPaneItem extends BasePaneItem {
32+
type: "divider";
33+
}
34+
3135
interface ButtonPaneItem extends BasePaneItem, IButtonPropertyItemOptions {
3236
type: "button";
3337
pressed: () => void;
@@ -78,7 +82,18 @@ interface SubPane extends BasePaneItem, ISubPanePropertyItemOptions {
7882
items: PaneItem[] | { build: (pane: UIPane) => void };
7983
}
8084

81-
export type PaneItem = ButtonPaneItem | SliderPaneItem | TogglePaneItem | DropdownPaneItem | ComboBoxPaneItem | ToggleGroupPaneItem | Vector3PaneItem | TextAreaPaneItem | LabelPaneItem | SubPane;
85+
export type PaneItem =
86+
| DividerPaneItem
87+
| ButtonPaneItem
88+
| SliderPaneItem
89+
| TogglePaneItem
90+
| DropdownPaneItem
91+
| ComboBoxPaneItem
92+
| ToggleGroupPaneItem
93+
| Vector3PaneItem
94+
| TextAreaPaneItem
95+
| LabelPaneItem
96+
| SubPane;
8297

8398
export interface PaneLayout extends ISubPanePropertyItemOptions {
8499
items: PaneItem[] | { build: (pane: UIPane) => void };
@@ -168,6 +183,9 @@ export class UIPane {
168183
const item = items[i];
169184
const id = item.uniqueId ?? i;
170185
switch (item.type) {
186+
case "divider":
187+
this.properties[id] = this.pane.addDivider();
188+
break;
171189
case "button":
172190
this.properties[id] = this.pane.addButton(item.pressed, item);
173191
break;

0 commit comments

Comments
 (0)