UNPKG

@minecraft/creator-tools

Version:

Minecraft Creator Tools command line and libraries.

1,039 lines 121 kB
{ "camera-grapple.ts": { "description": "camera-grapple.ts ", "prefix": ["mc"], "body": [ "// Copyright (c) Mojang AB. All rights reserved.", "import { Vector3Utils } from '@minecraft/math';", "import {", " ActionTypes,", " CursorControlMode,", " EditorInputContext,", " IPlayerUISession,", " KeyboardKey,", " registerEditorExtension,", "} from '@minecraft/server-editor';", "import { EasingType, TicksPerSecond, Vector3, system } from '@minecraft/server';", "interface GrappleStorage {", " latestRunId?: number;", "}", "type GrappleSession = IPlayerUISession<GrappleStorage>;", "function flyCameraToTarget(uiSession: GrappleSession, viewTarget: Vector3, radius: number) {", " if (uiSession.scratchStorage) {", " const player = uiSession.extensionContext.player;", " // This is imperfect and causes a visible pop. Would be better if we could get the player's exact eye height", " const eyeHeight = Vector3Utils.subtract(player.getHeadLocation(), player.location);", " const viewVector = player.getViewDirection();", " radius = Math.max(radius, 1);", " // FOV in first_person.json is 66 degrees", " const halfFOV = 66 / 2;", " // Compute adjacent side of triangle (distance) when opposite side is radius", " const distanceAway = radius / Math.tan((halfFOV * Math.PI) / 180);", " const destCameraLocation = Vector3Utils.subtract(viewTarget, Vector3Utils.scale(viewVector, distanceAway));", " const destPlayerLocation = Vector3Utils.subtract(destCameraLocation, eyeHeight);", " const easeTimeInSeconds = 1.5;", " // Unhook camera and have it start moving to the new location", " player.camera.setCamera('minecraft:free', {", " rotation: { x: player.getRotation().x, y: player.getRotation().y },", " location: { x: destCameraLocation.x, y: destCameraLocation.y, z: destCameraLocation.z },", " easeOptions: {", " easeTime: easeTimeInSeconds,", " easeType: EasingType.InOutQuad,", " },", " });", " uiSession.scratchStorage.latestRunId = system.runTimeout(() => {", " // Move the player to the final location and re-hook the camera to it", " player.teleport(destPlayerLocation);", " player.camera.clear();", " }, easeTimeInSeconds * TicksPerSecond);", " }", "}", "/**", " * Provides a 'Grapple' extension for quickly moving the player around the world", " * @beta", " */", "export function registerCameraGrapple() {", " registerEditorExtension(", " 'camera-grapple-sample',", " (uiSession: GrappleSession) => {", " uiSession.log.debug(`Initializing extension [${uiSession.extensionContext.extensionInfo.name}]`);", " const grappleAction = uiSession.actionManager.createAction({", " actionType: ActionTypes.NoArgsAction,", " onExecute: () => {", " // don't execute if there is already a command running as this can be visually disorienting", " if (uiSession.scratchStorage?.latestRunId) {", " return;", " }", " let destBlockLoc: Vector3 | undefined = undefined;", " const cursor = uiSession.extensionContext.cursor;", " // Fixed cursor mode will default to the player view direction", " if (cursor.isVisible && cursor.getProperties().controlMode !== CursorControlMode.Fixed) {", " destBlockLoc = cursor.getPosition();", " } else {", " const result = uiSession.extensionContext.player.getBlockFromViewDirection();", " if (!result) {", " uiSession.log.warning('No Block Found. Aborting Grapple');", " return;", " }", " destBlockLoc = result?.block.location;", " }", " // Location of the center of the block", " const viewTarget = Vector3Utils.add(destBlockLoc, { x: 0.5, y: 0.5, z: 0.5 });", " flyCameraToTarget(uiSession, viewTarget, 2);", " },", " });", " const frameAction = uiSession.actionManager.createAction({", " actionType: ActionTypes.NoArgsAction,", " onExecute: () => {", " // don't execute if there is already a command running as this can be visually disorienting", " if (uiSession.scratchStorage?.latestRunId) {", " return;", " }", " const selection = uiSession.extensionContext.selectionManager.selection;", " if (selection.isEmpty) {", " return;", " }", " const bounds = selection.getBoundingBox();", " bounds.max = Vector3Utils.add(bounds.max, { x: 1, y: 1, z: 1 });", " const halfSize = Vector3Utils.scale(Vector3Utils.subtract(bounds.max, bounds.min), 0.5);", " const viewTarget = Vector3Utils.add(bounds.min, halfSize);", " const radius = Math.sqrt(", " halfSize.x * halfSize.x + halfSize.y * halfSize.y + halfSize.z * halfSize.z", " );", " flyCameraToTarget(uiSession, viewTarget, radius);", " },", " });", " uiSession.inputManager.registerKeyBinding(", " EditorInputContext.GlobalToolMode,", " grappleAction,", " { key: KeyboardKey.KEY_G },", " {", " uniqueId: 'editorSamples:grapple:flyToCursor',", " label: 'sample.cameragrapple.keyBinding.flyToCursor',", " }", " );", " uiSession.inputManager.registerKeyBinding(", " EditorInputContext.GlobalToolMode,", " frameAction,", " { key: KeyboardKey.KEY_F },", " {", " uniqueId: 'editorSamples:grapple:flyToSelection',", " label: 'sample.cameragrapple.keyBinding.flyToSelection',", " }", " );", " return [];", " },", " (uiSession: GrappleSession) => {", " uiSession.log.debug(", " `Shutting down extension [${uiSession.extensionContext.extensionInfo.name}] for player [${uiSession.extensionContext.player.name}]`", " );", " if (uiSession.scratchStorage?.latestRunId) {", " system.clearRun(uiSession.scratchStorage.latestRunId);", " uiSession.scratchStorage.latestRunId = undefined;", " }", " },", " {", " description: 'Camera Grapple Sample Extension',", " notes: 'by Jonas',", " }", " );", "}", "" ] }, "dye-brush.ts": { "description": "dye-brush.ts ", "prefix": ["mc"], "body": [ "// Copyright (c) Mojang AB. All rights reserved.", "import {", " ActionTypes,", " ColorPickerPropertyItemVariant,", " CursorTargetMode,", " IDropdownItem,", " IModalTool,", " IObservable,", " IPlayerUISession,", " makeObservable,", " ModalToolLifecycleEventPayload,", " MouseActionType,", " MouseInputType,", " MouseProps,", " registerEditorExtension,", " Selection,", "} from '@minecraft/server-editor';", "import {", " BlockVolume,", " BoundingBox,", " BoundingBoxUtils,", " RGBA,", " CompoundBlockVolumeAction,", " Dimension,", " Direction,", " EntityColorComponent,", " Player,", " Vector3,", " RGB,", "} from '@minecraft/server';", "import { Vector3Utils, VECTOR3_UP } from '@minecraft/math';", "// Color identifiers expected by EntityColorComponent", "enum EntityColor {", " White = 0,", " Orange = 1,", " Magenta = 2,", " LightBlue = 3,", " Yellow = 4,", " LightGreen = 5,", " Pink = 6,", " Gray = 7,", " Silver = 8,", " Cyan = 9,", " Purple = 10,", " Blue = 11,", " Brown = 12,", " Green = 13,", " Red = 14,", " Black = 15,", "}", "const directionLookup: Record<Direction, Vector3> = {", " [Direction.North]: { x: 0, y: 0, z: 1 },", " [Direction.East]: { x: -1, y: 0, z: 0 },", " [Direction.South]: { x: 0, y: 0, z: -1 },", " [Direction.West]: { x: 1, y: 0, z: 0 },", " [Direction.Up]: { x: 0, y: 1, z: 0 },", " [Direction.Down]: { x: 0, y: -1, z: 0 },", "};", "const directionToQuadrant: Record<Direction, number> = {", " [Direction.North]: 0,", " [Direction.East]: 1,", " [Direction.South]: 2,", " [Direction.West]: 3,", " [Direction.Up]: 4,", " [Direction.Down]: 5,", "};", "const quadrantToDirection: Record<number, Direction> = {", " [0]: Direction.North,", " [1]: Direction.East,", " [2]: Direction.South,", " [3]: Direction.West,", " [4]: Direction.Up,", " [5]: Direction.Down,", "};", "export function getRotationCorrectedDirection(rotationY: number, realDirection: Direction): Direction {", " if (realDirection === Direction.Up || realDirection === Direction.Down) {", " return realDirection;", " }", " const quadrant = directionToQuadrant[realDirection];", " const rotatedQuadrant = Math.floor(((rotationY + 405 + quadrant * 90) % 360) / 90);", " const rotatedDirection = quadrantToDirection[rotatedQuadrant];", " return rotatedDirection;", "}", "export function getRotationCorrectedDirectionVector(rotationY: number, realDirection: Direction): Vector3 {", " const relativeDirection = getRotationCorrectedDirection(rotationY, realDirection);", " return directionLookup[relativeDirection];", "}", "// Calculate nearest entity color to an RGBA color", "function findClosestColor(targetColor: RGBA, colorPalette: Map<EntityColor, RGB>): EntityColor {", " let minDistance = Number.MAX_VALUE;", " let closestColor: EntityColor = EntityColor.White;", " colorPalette.forEach((paletteColor, color) => {", " const distance = Math.sqrt(", " Math.pow(targetColor.red - paletteColor.red, 2) +", " Math.pow(targetColor.green - paletteColor.green, 2) +", " Math.pow(targetColor.blue - paletteColor.blue, 2)", " );", " if (distance < minDistance) {", " minDistance = distance;", " closestColor = color;", " }", " });", " return closestColor;", "}", "const colorPalette = new Map<EntityColor, RGB>([", " [EntityColor.White, { red: 1, green: 1, blue: 1 }],", " [EntityColor.Orange, { red: 0.95, green: 0.459, blue: 0 }],", " [EntityColor.Magenta, { red: 0.94, green: 0, blue: 0.9 }],", " [EntityColor.LightBlue, { red: 0, green: 0.85, blue: 0.95 }],", " [EntityColor.Yellow, { red: 0.85, green: 0.95, blue: 0 }],", " [EntityColor.LightGreen, { red: 0, green: 0.95, blue: 0.6 }],", " [EntityColor.Pink, { red: 0.9, green: 0.65, blue: 0.85 }],", " [EntityColor.Gray, { red: 0.6, green: 0.6, blue: 0.6 }],", " [EntityColor.Silver, { red: 0.75, green: 0.75, blue: 0.75 }],", " [EntityColor.Cyan, { red: 0, green: 0.9, blue: 0.9 }],", " [EntityColor.Purple, { red: 0.45, green: 0, blue: 0.9 }],", " [EntityColor.Blue, { red: 0, green: 0, blue: 1 }],", " [EntityColor.Brown, { red: 0.8, green: 0.5, blue: 0.1 }],", " [EntityColor.Green, { red: 0, green: 1, blue: 0 }],", " [EntityColor.Red, { red: 1, green: 0, blue: 0 }],", " [EntityColor.Black, { red: 0, green: 0, blue: 0 }],", "]);", "interface DyeBrushStorage {", " previewSelection: Selection;", " lastVolumePlaced?: BoundingBox;", " currentColor: EntityColor;", " brushColor: IObservable<RGBA>;", " brushSize: number;", "}", "type DyeBrushSession = IPlayerUISession<DyeBrushStorage>;", "function onColorUpdated(newColor: RGBA, uiSession: DyeBrushSession) {", " if (uiSession.scratchStorage) {", " uiSession.scratchStorage.previewSelection.setFillColor(newColor);", " uiSession.scratchStorage.previewSelection.setOutlineColor({ ...newColor, alpha: 1 });", " const cursorProps = uiSession.extensionContext.cursor.getProperties();", " cursorProps.outlineColor = { ...newColor, alpha: 1 };", " cursorProps.targetMode = CursorTargetMode.Face;", " uiSession.extensionContext.cursor.setProperties(cursorProps);", " }", "}", "function addDyeBrushPane(uiSession: DyeBrushSession, tool: IModalTool) {", " if (!uiSession.scratchStorage) {", " throw Error('UI Session storage should exist');", " }", " const brushColor = uiSession.scratchStorage.brushColor;", " const brushSize = uiSession.scratchStorage.brushSize;", " const pane = uiSession.createPropertyPane({", " title: 'sample.dyeBrush.pane.title',", " });", " const entityBrush = makeObservable(EntityColor.White);", " onColorUpdated(brushColor.value, uiSession);", " pane.addDropdown(entityBrush, {", " title: 'Brush',", " entries: Object.values(EntityColor).reduce<IDropdownItem[]>((list, dye, index) => {", " if (typeof dye === 'string') {", " list.push({", " label: dye,", " value: index,", " });", " }", " return list;", " }, []),", " onChange: (newVal: number) => {", " if (newVal in EntityColor) {", " const foundColor = colorPalette.get(newVal);", " if (foundColor) {", " brushColor.set({ ...foundColor, alpha: brushColor.value.alpha });", " }", " onColorUpdated(brushColor.value, uiSession);", " }", " },", " });", " pane.addColorPicker(brushColor, {", " variant: ColorPickerPropertyItemVariant.Expanded,", " onChange: (color: RGBA) => {", " entityBrush.set(findClosestColor(color, colorPalette));", " onColorUpdated(brushColor.value, uiSession);", " },", " });", " tool.bindPropertyPane(pane);", " const onExecuteBrush = () => {", " if (uiSession.scratchStorage === undefined) {", " uiSession.log.error('Storage was not initialized.');", " return;", " }", " const previewSelection = uiSession.scratchStorage.previewSelection;", " const player = uiSession.extensionContext.player;", " const targetBlock = player.dimension.getBlock(uiSession.extensionContext.cursor.getPosition());", " if (targetBlock === undefined) {", " return;", " }", " const rotationY = uiSession.extensionContext.player.getRotation().y;", " const directionRight = getRotationCorrectedDirectionVector(rotationY, Direction.East);", " const directionForward = getRotationCorrectedDirectionVector(rotationY, Direction.South);", " const relativeDirection = Vector3Utils.add(Vector3Utils.add(directionRight, directionForward), VECTOR3_UP);", " const sizeHalf = Math.floor(brushSize / 2);", " let fromOffset = Vector3Utils.scale(relativeDirection, -sizeHalf);", " const toOffset = Vector3Utils.scale(relativeDirection, brushSize - 1);", " const isEven = brushSize % 2 === 0;", " if (isEven) {", " fromOffset = Vector3Utils.add(fromOffset, VECTOR3_UP);", " }", " const location = targetBlock.location;", " const from: Vector3 = {", " x: location.x + fromOffset.x,", " y: location.y + fromOffset.y,", " z: location.z + fromOffset.z,", " };", " const to: Vector3 = { x: from.x + toOffset.x, y: from.y + toOffset.y, z: from.z + toOffset.z };", " const blockVolume = new BlockVolume(from, to);", " const bounds = blockVolume.getBoundingBox();", " if (uiSession.scratchStorage.lastVolumePlaced) {", " if (BoundingBoxUtils.equals(uiSession.scratchStorage.lastVolumePlaced, bounds)) {", " return;", " }", " }", " previewSelection.pushVolume({ action: CompoundBlockVolumeAction.Add, volume: blockVolume });", " uiSession.scratchStorage.lastVolumePlaced = bounds;", " };", " const mouseButtonAction = uiSession.actionManager.createAction({", " actionType: ActionTypes.MouseRayCastAction,", " onExecute: (_, mouseProps: MouseProps) => {", " if (uiSession.scratchStorage === undefined) {", " uiSession.log.error('Storage was not initialized.');", " return;", " }", " if (mouseProps.mouseAction === MouseActionType.LeftButton) {", " if (mouseProps.inputType === MouseInputType.ButtonDown) {", " uiSession.scratchStorage.previewSelection.clear();", " onExecuteBrush();", " } else if (mouseProps.inputType === MouseInputType.ButtonUp) {", " const player: Player = uiSession.extensionContext.player;", " const dimension: Dimension = player.dimension;", " const iterator = uiSession.scratchStorage.previewSelection.getBlockLocationIterator();", " for (const pos of iterator) {", " const entities = dimension.getEntities({ location: pos, closest: 1 });", " for (const entity of entities) {", " const colorComp = entity.getComponent('minecraft:color') as EntityColorComponent;", " if (colorComp) {", " colorComp.value = entityBrush.value;", " }", " }", " }", " uiSession.scratchStorage.previewSelection.clear();", " }", " }", " },", " });", " tool.registerMouseButtonBinding(mouseButtonAction);", " const executeBrushRayAction = uiSession.actionManager.createAction({", " actionType: ActionTypes.MouseRayCastAction,", " onExecute: (_, mouseProps: MouseProps) => {", " if (mouseProps.inputType === MouseInputType.Drag) {", " onExecuteBrush();", " }", " },", " });", " tool.registerMouseDragBinding(executeBrushRayAction);", " // Example for adding mouse wheel", " const executeBrushSizeAction = uiSession.actionManager.createAction({", " actionType: ActionTypes.MouseRayCastAction,", " onExecute: (_, mouseProps: MouseProps) => {", " if (mouseProps.mouseAction === MouseActionType.Wheel) {", " if (mouseProps.inputType === MouseInputType.WheelOut) {", " if (entityBrush.value > 0) {", " entityBrush.set(entityBrush.value - 1);", " }", " } else if (mouseProps.inputType === MouseInputType.WheelIn) {", " if (entityBrush.value < 15) {", " entityBrush.set(entityBrush.value + 1);", " }", " }", " onColorUpdated(brushColor.value, uiSession);", " }", " },", " });", " tool.registerMouseWheelBinding(executeBrushSizeAction);", " tool.onModalToolActivation.subscribe((evt: ModalToolLifecycleEventPayload) => {", " if (evt.isActiveTool) {", " onColorUpdated(brushColor.value, uiSession);", " }", " uiSession.scratchStorage?.previewSelection?.clear();", " });", " pane.hide();", "}", "export function addDyeBrushTool(uiSession: DyeBrushSession) {", " const tool = uiSession.toolRail.addTool({", " title: 'sample.dyebrush.tool.title',", " tooltip: 'sample.dyebrush.tool.tooltip',", " icon: 'pack://textures/dye-brush.png',", " });", " return tool;", "}", "export function registerDyeBrushExtension() {", " registerEditorExtension<DyeBrushStorage>(", " 'dye-brush-sample',", " (uiSession: IPlayerUISession<DyeBrushStorage>) => {", " uiSession.log.debug(`Initializing extension [${uiSession.extensionContext.extensionInfo.name}]`);", " const previewSelection = uiSession.extensionContext.selectionManager.create();", " previewSelection.visible = true;", " const storage: DyeBrushStorage = {", " previewSelection: previewSelection,", " currentColor: EntityColor.White,", " brushColor: makeObservable<RGBA>({ red: 1, green: 1, blue: 1, alpha: 0.5 }),", " brushSize: 4,", " };", " uiSession.scratchStorage = storage;", " const cubeBrushTool = addDyeBrushTool(uiSession);", " addDyeBrushPane(uiSession, cubeBrushTool);", " return [];", " },", " (uiSession: IPlayerUISession<DyeBrushStorage>) => {", " uiSession.log.debug(`Shutting down extension [${uiSession.extensionContext.extensionInfo.name}] `);", " },", " {", " description: 'Dye Brush Sample Extension',", " notes: 'By Eser',", " }", " );", "}", "" ] }, "farm-generator.ts": { "description": "farm-generator.ts ", "prefix": ["mc"], "body": [ "// Copyright (c) Mojang AB. All rights reserved.", "import { VECTOR3_ZERO } from '@minecraft/math';", "import {", " ActionTypes,", " EditorInputContext,", " IModalTool,", " IObservable,", " IPlayerUISession,", " InputModifier,", " KeyboardKey,", " MouseActionType,", " MouseInputType,", " MouseProps,", " Ray,", " makeObservable,", " registerEditorExtension,", "} from '@minecraft/server-editor';", "import { Player, Vector3 } from '@minecraft/server';", "import { MinecraftBlockTypes, MinecraftEntityTypes } from '@minecraft/vanilla-data';", "type CommonSettingsType = {", " farmWidth: IObservable<number>;", " farmLength: IObservable<number>;", " irrigation: IObservable<boolean>;", " fenceType: IObservable<number>;", "};", "type CropSettingsType = {", " wheat: IObservable<boolean>;", " pumpkin: IObservable<boolean>;", " potato: IObservable<boolean>;", " carrot: IObservable<boolean>;", " beetroot: IObservable<boolean>;", "};", "type AnimalSettingsType = {", " pig: IObservable<boolean>;", " sheep: IObservable<boolean>;", " cow: IObservable<boolean>;", "};", "function getRandomInt(upper: number) {", " return Math.floor(Math.random() * (upper + 1));", "}", "function fenceTypeToBlockType(fenceType: number): string {", " switch (fenceType) {", " case 0:", " return MinecraftBlockTypes.OakFence;", " case 1:", " return MinecraftBlockTypes.BirchFence;", " case 2:", " return MinecraftBlockTypes.AcaciaFence;", " case 3:", " return MinecraftBlockTypes.BambooFence;", " case 4:", " return MinecraftBlockTypes.CherryFence;", " case 5:", " return MinecraftBlockTypes.JungleFence;", " case 6:", " return MinecraftBlockTypes.SpruceFence;", " case 7:", " return MinecraftBlockTypes.WarpedFence;", " case 8:", " return MinecraftBlockTypes.CrimsonFence;", " default:", " return MinecraftBlockTypes.OakFence;", " }", "}", "const buildFarm = (", " targetCorner: Vector3,", " x: number,", " z: number,", " length: number,", " width: number,", " possibleAnimals: MinecraftEntityTypes[],", " possibleCrops: string[],", " player: Player,", " commonSettings: CommonSettingsType", ") => {", " let didPlaceAnimal = false;", " for (let i = 0; i < width; i++) {", " for (let j = length - 1; j > -1; j--) {", " const xOffset = i * x;", " const zOffset = z * j;", " const location: Vector3 = { x: targetCorner.x + xOffset, y: targetCorner.y, z: targetCorner.z + zOffset };", " const locationAbove: Vector3 = {", " x: targetCorner.x + xOffset,", " y: targetCorner.y + 1,", " z: targetCorner.z + zOffset,", " };", " const block = player.dimension.getBlock(location);", " const blockAbove = player.dimension.getBlock(locationAbove);", " const isBorder = i === 0 || i === width - 1 || j === 0 || j === length - 1;", " if (xOffset % 3 === 0 && !isBorder && commonSettings.irrigation.value) {", " block?.setType(MinecraftBlockTypes.Water);", " } else {", " block?.setType(MinecraftBlockTypes.Farmland);", " }", " if (isBorder) {", " blockAbove?.setType(fenceTypeToBlockType(commonSettings.fenceType.value));", " } else if (possibleAnimals.length > 0 && getRandomInt(5) === 5) {", " const animal = getRandomInt(possibleAnimals.length - 1);", " const entityType = possibleAnimals[animal];", " player.dimension.spawnEntity(entityType, blockAbove?.location ?? VECTOR3_ZERO, {", " initialPersistence: true,", " });", " didPlaceAnimal = true;", " } else if (!block?.isLiquid && possibleCrops.length > 0) {", " const crop = getRandomInt(possibleCrops.length - 1);", " const blockType = possibleCrops[crop];", " blockAbove?.setType(blockType);", " }", " }", " }", " // Guarantee there is at least one animal spawned if we haven't placed one yet and there is room to place one", " if (!didPlaceAnimal && possibleAnimals.length > 0 && width > 2 && length > 2) {", " const locationAbove: Vector3 = {", " x: targetCorner.x + x,", " y: targetCorner.y + 1,", " z: targetCorner.z + z,", " };", " const blockAbove = player.dimension.getBlock(locationAbove);", " const animal = getRandomInt(possibleAnimals.length - 1);", " const entityType = possibleAnimals[animal];", " player.dimension.spawnEntity(entityType, blockAbove?.location ?? VECTOR3_ZERO, { initialPersistence: true });", " }", "};", "function addFarmGeneratorSettingsPane(uiSession: IPlayerUISession, tool: IModalTool) {", " const windowPane = uiSession.createPropertyPane({", " title: 'sample.farmgenerator.pane.title',", " });", " const cropPane = windowPane.createSubPane({", " title: 'sample.farmgenerator.pane.crops.title',", " });", " const animalPane = windowPane.createSubPane({", " title: 'sample.farmgenerator.pane.animals.title',", " });", " const commonSettings: CommonSettingsType = {", " farmWidth: makeObservable(10),", " farmLength: makeObservable(10),", " irrigation: makeObservable(false),", " fenceType: makeObservable(0),", " };", " const cropSettings: CropSettingsType = {", " wheat: makeObservable(false),", " pumpkin: makeObservable(false),", " potato: makeObservable(false),", " carrot: makeObservable(false),", " beetroot: makeObservable(false),", " };", " const animalSettings: AnimalSettingsType = {", " pig: makeObservable(false),", " sheep: makeObservable(false),", " cow: makeObservable(false),", " };", " const onExecuteGenerator = (ray?: Ray) => {", " const player: Player = uiSession.extensionContext.player;", " // Use the mouse ray if it is available", " const raycastResult =", " ray !== undefined", " ? player.dimension.getBlockFromRay(ray.location, ray.direction)", " : player.getBlockFromViewDirection();", " if (!raycastResult) {", " uiSession.log.error('No block from view vector');", " return;", " }", " const targetBlock = raycastResult.block;", " let targetCorner: Vector3 = { x: targetBlock.location.x, y: targetBlock.location.y, z: targetBlock.location.z };", " const possibleCrops: string[] = [];", " if (cropSettings.beetroot.value) {", " possibleCrops.push(MinecraftBlockTypes.Beetroot);", " }", " if (cropSettings.carrot.value) {", " possibleCrops.push(MinecraftBlockTypes.Carrots);", " }", " if (cropSettings.pumpkin.value) {", " possibleCrops.push(MinecraftBlockTypes.Pumpkin);", " }", " if (cropSettings.wheat.value) {", " possibleCrops.push(MinecraftBlockTypes.Wheat);", " }", " if (cropSettings.potato.value) {", " possibleCrops.push(MinecraftBlockTypes.Potatoes);", " }", " const possibleAnimals: MinecraftEntityTypes[] = [];", " if (animalSettings.sheep.value) {", " possibleAnimals.push(MinecraftEntityTypes.Sheep);", " }", " if (animalSettings.cow.value) {", " possibleAnimals.push(MinecraftEntityTypes.Cow);", " }", " if (animalSettings.pig.value) {", " possibleAnimals.push(MinecraftEntityTypes.Pig);", " }", " let x = 1;", " let z = 1;", " let length = commonSettings.farmLength.value;", " let width = commonSettings.farmWidth.value;", " if (Math.round(player.getViewDirection().z) === -1) {", " targetCorner = {", " x: targetCorner.x + (commonSettings.farmWidth.value / 2 - 1),", " y: targetCorner.y,", " z: targetCorner.z - (commonSettings.farmLength.value / 2 - 1),", " };", " uiSession.log.info('Facing north');", " x = -1;", " } else if (Math.round(player.getViewDirection().x) === 1) {", " targetCorner = {", " x: targetCorner.x + (commonSettings.farmWidth.value / 2 - 1),", " y: targetCorner.y,", " z: targetCorner.z + (commonSettings.farmLength.value / 2 - 1),", " };", " uiSession.log.info('Facing east');", " length = commonSettings.farmWidth.value;", " width = commonSettings.farmLength.value;", " x = -1;", " z = -1;", " }", " if (Math.round(player.getViewDirection().z) === 1) {", " targetCorner = {", " x: targetCorner.x - (commonSettings.farmWidth.value / 2 - 1),", " y: targetCorner.y,", " z: targetCorner.z + (commonSettings.farmLength.value / 2 - 1),", " };", " uiSession.log.info('Facing south');", " z = -1;", " } else if (Math.round(player.getViewDirection().x) === -1) {", " targetCorner = {", " x: targetCorner.x - (commonSettings.farmWidth.value / 2 - 1),", " y: targetCorner.y,", " z: targetCorner.z - (commonSettings.farmLength.value / 2 - 1),", " };", " uiSession.log.info('Facing west');", " length = commonSettings.farmWidth.value;", " width = commonSettings.farmLength.value;", " }", " buildFarm(targetCorner, x, z, length, width, possibleAnimals, possibleCrops, player, commonSettings);", " };", " // Create an action that will be executed on left mouse click", " const executeMouseAction = uiSession.actionManager.createAction({", " actionType: ActionTypes.MouseRayCastAction,", " onExecute: (mouseRay: Ray, mouseProps: MouseProps) => {", " if (mouseProps.mouseAction === MouseActionType.LeftButton && mouseProps.inputType === MouseInputType.ButtonDown) {", " onExecuteGenerator(mouseRay);", " }", " },", " });", " // Create and an action that will be executed on CTRL + P", " const executeKeyAction = uiSession.actionManager.createAction({", " actionType: ActionTypes.NoArgsAction,", " onExecute: () => {", " onExecuteGenerator();", " },", " });", " // Register actions as input bindings to tool context", " tool.registerKeyBinding(", " executeKeyAction,", " { key: KeyboardKey.KEY_P, modifier: InputModifier.Control },", " {", " uniqueId: 'editorSamples:farmGenerator:place',", " label: 'sample.farmgenerator.keyBinding.place',", " }", " );", " tool.registerMouseButtonBinding(executeMouseAction);", " windowPane.addNumber(commonSettings.farmLength, {", " title: 'sample.farmgenerator.pane.length',", " min: 2,", " max: 20,", " isInteger: true,", " });", " windowPane.addNumber(commonSettings.farmWidth, {", " title: 'sample.farmgenerator.pane.width',", " min: 2,", " max: 20,", " isInteger: true,", " });", " windowPane.addDropdown(commonSettings.fenceType, {", " title: 'sample.farmgenerator.pane.fence',", " enable: true,", " entries: [", " {", " label: 'Oak',", " value: 0,", " },", " {", " label: 'Birch',", " value: 1,", " },", " {", " label: 'Acacia',", " value: 2,", " },", " {", " label: 'Bamboo',", " value: 3,", " },", " {", " label: 'Cherry',", " value: 4,", " },", " {", " label: 'Jungle',", " value: 5,", " },", " {", " label: 'Spruce',", " value: 6,", " },", " {", " label: 'Warped',", " value: 7,", " },", " {", " label: 'Crimson',", " value: 8,", " },", " ],", " });", " windowPane.addBool(commonSettings.irrigation, {", " title: 'sample.farmgenerator.pane.irrigation',", " tooltip: 'sample.farmgenerator.pane.irrigation.tooltip',", " });", " cropPane.addBool(cropSettings.wheat, {", " title: 'sample.farmgenerator.pane.crops.wheat',", " });", " cropPane.addBool(cropSettings.potato, {", " title: 'sample.farmgenerator.pane.crops.potato',", " });", " cropPane.addBool(cropSettings.beetroot, {", " title: 'sample.farmgenerator.pane.crops.beets',", " });", " cropPane.addBool(cropSettings.pumpkin, {", " title: 'sample.farmgenerator.pane.crops.pumpkin',", " });", " cropPane.addBool(cropSettings.carrot, {", " title: 'sample.farmgenerator.pane.crops.carrot',", " });", " animalPane.addBool(animalSettings.cow, {", " title: 'sample.farmgenerator.pane.animals.cow',", " });", " animalPane.addBool(animalSettings.sheep, {", " title: 'sample.farmgenerator.pane.animals.sheep',", " });", " animalPane.addBool(animalSettings.pig, {", " title: 'sample.farmgenerator.pane.animals.pig',", " });", " tool.bindPropertyPane(windowPane);", "}", "/**", " * Create a new tool rail item for farm generator", " */", "function addFarmGeneratorTool(uiSession: IPlayerUISession) {", " // Create action", " const toolToggleAction = uiSession.actionManager.createAction({", " actionType: ActionTypes.NoArgsAction,", " onExecute: () => {", " uiSession.toolRail.setSelectedToolId(tool.id);", " },", " });", " const tool = uiSession.toolRail.addTool(", " {", " title: 'sample.farmgenerator.tool.title',", " icon: 'pack://textures/farm-generator.png',", " tooltip: 'sample.farmgenerator.tool.tooltip',", " inputContextId: 'editorSamples:farmGenerator',", " inputContextLabel: 'sample.farmgenerator.tool.title',", " },", " toolToggleAction", " );", " // Register a global shortcut (CTRL + SHIFT + P) to select the tool", " uiSession.inputManager.registerKeyBinding(", " EditorInputContext.GlobalToolMode,", " toolToggleAction,", " { key: KeyboardKey.KEY_F, modifier: InputModifier.Control | InputModifier.Shift },", " {", " uniqueId: 'editorSamples:farmGenerator:toggleTool',", " label: 'sample.farmgenerator.keyBinding.toggleTool',", " }", " );", " return tool;", "}", "/**", " * Register Farm Generator extension", " */", "export function registerFarmGeneratorExtension() {", " registerEditorExtension(", " 'FarmGenerator-sample',", " (uiSession: IPlayerUISession) => {", " uiSession.log.debug(`Initializing [${uiSession.extensionContext.extensionInfo.name}] extension`);", " // Add tool to tool rail", " const farmGeneratorTool = addFarmGeneratorTool(uiSession);", " // Create settings pane/window", " addFarmGeneratorSettingsPane(uiSession, farmGeneratorTool);", " return [];", " },", " (uiSession: IPlayerUISession) => {", " uiSession.log.debug(`Initializing [${uiSession.extensionContext.extensionInfo.name}] extension`);", " },", " {", " description: 'Farm Generator Sample Extension',", " notes: 'by Molly',", " }", " );", "}", "" ] }, "goto-mark.ts": { "description": "goto-mark.ts ", "prefix": ["mc"], "body": [ "// Copyright (c) Mojang AB. All rights reserved.", "import {", " ActionTypes,", " IButtonPropertyItem,", " IDropdownItem,", " IDropdownPropertyItem,", " IModalTool,", " IObservable,", " IPlayerUISession,", " IPropertyPane,", " UserDefinedTransactionHandle,", " bindDataSource,", " makeObservable,", " registerEditorExtension,", " registerUserDefinedTransactionHandler,", "} from '@minecraft/server-editor';", "import { Vector3, system } from '@minecraft/server';", "const storedLocationDynamicPropertyName = 'goto-mark:storedLocations'; // The key of the stored location dynamic property", "const storedLocationNameMaxLength = 16; // This is the maximum length of the name of a stored location", "const storedLocationsMax = 9; // The maximum number of stored locations", "type GotoTeleportTransactionPayload = {", " current: Vector3;", " destination: Vector3;", "};", "// The stored location data structure that represents each of the stored locations", "// this is also the JSON format that is stored in the dynamic property", "type LocationData = {", " location: Vector3;", " name: string;", "};", "// UI Pane data for the whole extension pane", "type ParentPaneDataSourceType = {", " playerLocation: Vector3;", "};", "// UI Pane data for the sub pane with the stored locations", "type LocationPaneDataSourceType = {", " newName: IObservable<string>;", "};", "// Extension storage data which is pertinent to the the player's context of this extension", "type ExtensionStorage = {", " tool?: IModalTool; // The tool handle for the extension", " previousLocation: Vector3; // The players last recorded position", " updateHandle?: number; // The handle for the repeating interval that updates the player position", " parentPaneDataSource?: ParentPaneDataSourceType; // The data source for the parent pane", " parentPane?: IPropertyPane; // The parent pane", " dropdownMenu?: IDropdownPropertyItem; // The dropdown", " locationPaneDataSource?: LocationPaneDataSourceType; // The data source for the location pane", " storedLocations: LocationData[]; // The list of stored locations", " transactionHandler: UserDefinedTransactionHandle<GotoTeleportTransactionPayload>; // The transaction handler for the extension", " teleportButton?: IButtonPropertyItem;", "};", "// Handy helper to turn a Vector3 into a pretty string", "function vector3ToString(vec: Vector3): string {", " return `(${vec.x}, ${vec.y}, ${vec.z})`;", "}", "// Equality check for a Vector3", "function vector3Equals(vec1: Vector3, vec2: Vector3): boolean {", " return vec1.x === vec2.x && vec1.y === vec2.y && vec1.z === vec2.z;", "}", "// Truncate a Vector3 to the nearest block", "function vector3Truncate(vec: Vector3): Vector3 {", " const blockLocation: Vector3 = { x: Math.floor(vec.x), y: Math.floor(vec.y), z: Math.floor(vec.z) };", " return blockLocation;", "}", "function mapDropdownItems(storage: ExtensionStorage): IDropdownItem[] {", " return storage.storedLocations.map((v, index): IDropdownItem => {", " const item: IDropdownItem = {", " label: `${index + 1}: ${v.name} (${vector3ToString(v.location)})`,", " value: index,", " };", " return item;", " });", "}", "function createTransaction(uiSession: IPlayerUISession<ExtensionStorage>, current: Vector3, destination: Vector3) {", " const transactionPayload: GotoTeleportTransactionPayload = {", " current,", " destination,", " };", " if (!uiSession.scratchStorage) {", " return;", " }", " uiSession.extensionContext.transactionManager.openTransaction('goto position');", " uiSession.scratchStorage.transactionHandler.addUserDefinedOperation(transactionPayload, 'Goto(Teleport)');", " uiSession.extensionContext.transactionManager.commitOpenTransaction();", "}", "function teleportTo(uiSession: IPlayerUISession<ExtensionStorage>, destination: Vector3) {", " createTransaction(uiSession, uiSession.extensionContext.player.location, destination);", " uiSession.log.info(`Teleporting to location ${vector3ToString(destination)}`);", " try {", " uiSession.extensionContext.player.teleport(destination);", " } catch (e) {", " uiSession.log.error(`Teleport failed: ${e}`);", " }", "}", "// Add the extension to the tool rail and give it an icon", "function addExtensionTool(uiSession: IPlayerUISession<ExtensionStorage>): IModalTool {", " const tool = uiSession.toolRail.addTool({", " title: 'sample.gotomark.tool.title',", " icon: 'pack://textures/goto-mark.png',", " tooltip: 'Set or Jump to a stored location',", " });", " return tool;", "}", "function buildParentPane(uiSession: IPlayerUISession<ExtensionStorage>, storage: ExtensionStorage): IPropertyPane {", " const parentPane = uiSession.createPropertyPane({", " title: 'sample.gotomark.pane.title',", " });", " const currentLocation = vector3Truncate(uiSession.extensionContext.player.location);", " const initialPaneData: ParentPaneDataSourceType = {", " playerLocation: currentLocation,", " };", " storage.parentPaneDataSource = bindDataSource(parentPane, initialPaneData);", " storage.previousLocation = currentLocation;", " parentPane.addVector3_deprecated(storage.parentPaneDataSource, 'playerLocation', {", " title: 'sample.gotomark.pane.location',", " });", " // Run interval to refresh coordinate population", " // Issue with refresh on tick rate with show/hide", " storage.updateHandle = system.runInterval(() => {", " if (!storage.parentPaneDataSource) {", " return;", " }", " const currentLocation = vector3Truncate(uiSession.extensionContext.player.location);", " const previousLocation = vector3Truncate(storage.previousLocation);", " // Player hasn't moved - don't refresh", " if (vector3Equals(currentLocation, previousLocation) || !parentPane.visible) {", " return;", " }", " storage.previousLocation = currentLocation;", " storage.parentPaneDataSource.playerLocation = { ...currentLocation };", " }, 10);", " // Jump directly to the location specified in the Vector3 UI control", " parentPane.addButton(", " uiSession.actionManager.createAction({", " actionType: ActionTypes.NoArgsAction,", " onExecute: () => {", " if (!storage.parentPaneDataSource) {", " uiSession.log.error('An error occurred: No UI pane datasource could be found');", " return;", " }", " const panelLocation = storage.parentPaneDataSource.playerLocation;", " teleportTo(uiSession, panelLocation);", " },", " }),", " {", " title: 'sample.gotomark.pane.button.teleport',", " visible: true,", " }", " );", " parentPane.addDivider();", " // Set the players spawn location based on the current location (or the location typed into the", " // Vector3 UI control)", " parentPane.addButton(", " uiSession.actionManager.createAction({", " actionType: ActionTypes.NoArgsAction,", " onExecute: () => {", " if (!storage.parentPaneDataSource) {", " uiSession.log.error('An error occurred: No UI pane dataso