flexible instance movement
This commit is contained in:
@@ -3,6 +3,7 @@ import { BuilderOptions } from "./BuilderOptions";
|
|||||||
export class Builder {
|
export class Builder {
|
||||||
playerId;
|
playerId;
|
||||||
materialInstanceName = void 0;
|
materialInstanceName = void 0;
|
||||||
|
flexibleInstanceMovement = void 0;
|
||||||
|
|
||||||
constructor(playerId) {
|
constructor(playerId) {
|
||||||
this.playerId = playerId;
|
this.playerId = playerId;
|
||||||
@@ -15,4 +16,8 @@ export class Builder {
|
|||||||
setOption(optionId, value) {
|
setOption(optionId, value) {
|
||||||
return BuilderOptions.setValue(optionId, this.playerId, value);
|
return BuilderOptions.setValue(optionId, this.playerId, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
isFlexibleInstanceMoving() {
|
||||||
|
return this.flexibleInstanceMovement !== void 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -1,35 +1,125 @@
|
|||||||
|
import { InputPermissionCategory, world, system } from "@minecraft/server";
|
||||||
|
import { Outliner } from "../Outliner";
|
||||||
|
import { MENU_ITEM } from "../../commands/construct";
|
||||||
|
import { Vector } from "../../lib/Vector";
|
||||||
|
import { PlayerMovement } from "../PlayerMovement";
|
||||||
|
import { Builders } from "../Builder/Builders";
|
||||||
|
|
||||||
export class FlexibleInstanceMove {
|
export class FlexibleInstanceMove {
|
||||||
player;
|
player;
|
||||||
instance;
|
instance;
|
||||||
|
outliner;
|
||||||
|
currentInstanceLocation;
|
||||||
|
runner = void 0;
|
||||||
|
|
||||||
constructor(instance, player) {
|
constructor(instance, player) {
|
||||||
this.instance = instance;
|
this.instance = instance;
|
||||||
this.player = player;
|
this.player = player;
|
||||||
this.tryBeginFlexibleMove();
|
this.currentInstanceLocation = Vector.from(instance.getLocation().location);
|
||||||
|
this.onPlayerUseItemBound = this.onPlayerUseItem.bind(this);
|
||||||
|
this.onPlayerLeaveBound = this.onPlayerLeave.bind(this);
|
||||||
|
this.tryStart();
|
||||||
}
|
}
|
||||||
|
|
||||||
tryBeginFlexibleMove() {
|
tryStart() {
|
||||||
if (this.instance.isFlexMoving) {
|
if (this.instance.isFlexibleMoving()) {
|
||||||
this.feedback('§cThis instance is already being moved.');
|
this.sendFeedback('§cThis instance is already being moved.');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.beginFlexibleMove();
|
this.start();
|
||||||
}
|
}
|
||||||
|
|
||||||
beginFlexibleMove() {
|
start() {
|
||||||
// place structure in movement mode
|
this.prepInstanceForMovement();
|
||||||
this.instance.startFlexibleMove(this.player);
|
this.prepPlayerForMovement();
|
||||||
// lock player controls
|
this.runner = system.runInterval(this.onFlexibleMovementTick.bind(this));
|
||||||
// start handling player movement controls
|
|
||||||
}
|
}
|
||||||
|
|
||||||
endFlexibleMove() {
|
prepInstanceForMovement() {
|
||||||
this.instance.stopFlexibleMove();
|
this.instance.flexMovingPlayerId = this.player.id;
|
||||||
|
this.instance.disable();
|
||||||
|
const bounds = this.instance.getBounds();
|
||||||
|
const maxWorldLocation = this.currentInstanceLocation.add(Vector.from(bounds.max));
|
||||||
|
this.outliner = new Outliner(this.instance.getDimension(), this.currentInstanceLocation, maxWorldLocation, 1, 1);
|
||||||
|
this.outliner.startDraw();
|
||||||
}
|
}
|
||||||
|
|
||||||
feedback(str) {
|
prepPlayerForMovement() {
|
||||||
this.player.sendMessage(str);
|
this.allowPlayerMovement(false);
|
||||||
|
world.beforeEvents.itemUse.subscribe(this.onPlayerUseItemBound);
|
||||||
|
world.beforeEvents.playerLeave.unsubscribe(this.onPlayerLeaveBound);
|
||||||
|
this.sendFeedback(`§aNow moving "${this.instance.getName()}". Use the Construct item when finished.`);
|
||||||
|
}
|
||||||
|
|
||||||
|
onFlexibleMovementTick() {
|
||||||
|
const playerMovement = new PlayerMovement(this.player);
|
||||||
|
const instanceVelocity = this.calculateInstanceMovement(playerMovement);
|
||||||
|
this.move(instanceVelocity);
|
||||||
|
}
|
||||||
|
|
||||||
|
calculateInstanceMovement(playerMovement) {
|
||||||
|
const speedFactor = 0.5;
|
||||||
|
|
||||||
|
const viewDir = playerMovement.getMajorDirectionFacing();
|
||||||
|
const forward = new Vector(viewDir.x, viewDir.y, viewDir.z);
|
||||||
|
const right = new Vector(forward.z, 0, -forward.x);
|
||||||
|
const moveInput = playerMovement.getMovementVector();
|
||||||
|
let velocity = forward.multiply(moveInput.y).add(right.multiply(moveInput.x));
|
||||||
|
|
||||||
|
if (playerMovement.isJumping())
|
||||||
|
velocity.y += 1;
|
||||||
|
if (playerMovement.isSneaking())
|
||||||
|
velocity.y -= 1;
|
||||||
|
|
||||||
|
if (velocity.length > 0)
|
||||||
|
velocity = velocity.normalized;
|
||||||
|
return velocity.multiply(speedFactor);
|
||||||
|
}
|
||||||
|
|
||||||
|
move(instanceVelocity) {
|
||||||
|
this.currentInstanceLocation = this.currentInstanceLocation.add(instanceVelocity);
|
||||||
|
this.moveOutline();
|
||||||
|
}
|
||||||
|
|
||||||
|
moveOutline() {
|
||||||
|
const bounds = this.instance.getBounds();
|
||||||
|
const minWorldLocation = this.currentInstanceLocation.floor()
|
||||||
|
const maxWorldLocation = minWorldLocation.add(Vector.from(bounds.max));
|
||||||
|
this.outliner.setVertices(this.instance.getDimension(), minWorldLocation, maxWorldLocation);
|
||||||
|
}
|
||||||
|
|
||||||
|
onPlayerUseItem(event) {
|
||||||
|
if (!event.source || event.itemStack?.typeId !== MENU_ITEM) return;
|
||||||
|
event.cancel = true;
|
||||||
|
system.run(() => this.finish());
|
||||||
|
}
|
||||||
|
|
||||||
|
onPlayerLeave(event) {
|
||||||
|
if (event.player?.id === this.player.id)
|
||||||
|
system.run(() => this.finish());
|
||||||
|
}
|
||||||
|
|
||||||
|
finish() {
|
||||||
|
system.clearRun(this.runner);
|
||||||
|
this.outliner.stopDraw();
|
||||||
|
this.outliner = void 0;
|
||||||
|
this.instance.move(this.instance.getDimension().id, this.currentInstanceLocation);
|
||||||
|
this.instance.enable();
|
||||||
|
this.instance.flexMovingPlayerId = void 0;
|
||||||
|
this.allowPlayerMovement(true);
|
||||||
|
const builder = Builders.get(this.player.id);
|
||||||
|
builder.flexibleInstanceMovement = void 0;
|
||||||
|
world.beforeEvents.itemUse.unsubscribe(this.onPlayerUseItemBound);
|
||||||
|
world.beforeEvents.playerLeave.unsubscribe(this.onPlayerLeaveBound);
|
||||||
|
this.sendFeedback(`§aMoved "${this.instance.getName()}" to ${this.currentInstanceLocation.floor()}.`);
|
||||||
|
}
|
||||||
|
|
||||||
|
allowPlayerMovement(enable) {
|
||||||
|
const inputPermissions = this.player.inputPermissions;
|
||||||
|
inputPermissions.setPermissionCategory(InputPermissionCategory.Movement, enable);
|
||||||
|
}
|
||||||
|
|
||||||
|
sendFeedback(message) {
|
||||||
|
system.run(() => this.player.onScreenDisplay.setActionBar(message));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// if the player logs out, stop the move
|
|
||||||
@@ -5,6 +5,7 @@ import { InstanceButtons } from '../Enums/InstanceButtons';
|
|||||||
import { InstanceFormBuilder } from './InstanceFormBuilder';
|
import { InstanceFormBuilder } from './InstanceFormBuilder';
|
||||||
import { FormCancelationReason } from '@minecraft/server-ui';
|
import { FormCancelationReason } from '@minecraft/server-ui';
|
||||||
import { FlexibleInstanceMove } from './FlexibleInstanceMove';
|
import { FlexibleInstanceMove } from './FlexibleInstanceMove';
|
||||||
|
import { Builders } from '../Builder/Builders';
|
||||||
|
|
||||||
export class InstanceForm {
|
export class InstanceForm {
|
||||||
instanceName;
|
instanceName;
|
||||||
@@ -13,6 +14,7 @@ export class InstanceForm {
|
|||||||
InstanceButtons.NextLayer,
|
InstanceButtons.NextLayer,
|
||||||
InstanceButtons.PreviousLayer,
|
InstanceButtons.PreviousLayer,
|
||||||
InstanceButtons.Move,
|
InstanceButtons.Move,
|
||||||
|
InstanceButtons.FlexibleMove,
|
||||||
InstanceButtons.Settings,
|
InstanceButtons.Settings,
|
||||||
InstanceButtons.Statistics,
|
InstanceButtons.Statistics,
|
||||||
InstanceButtons.Materials,
|
InstanceButtons.Materials,
|
||||||
@@ -96,7 +98,7 @@ export class InstanceForm {
|
|||||||
this.instance.move(this.player.dimension.id, this.player.location);
|
this.instance.move(this.player.dimension.id, this.player.location);
|
||||||
break;
|
break;
|
||||||
case InstanceButtons.FlexibleMove:
|
case InstanceButtons.FlexibleMove:
|
||||||
new FlexibleInstanceMove(this.instance, this.player);
|
this.flexibleMovement();
|
||||||
break;
|
break;
|
||||||
case InstanceButtons.Settings:
|
case InstanceButtons.Settings:
|
||||||
this.settingsForm();
|
this.settingsForm();
|
||||||
@@ -135,6 +137,11 @@ export class InstanceForm {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
flexibleMovement() {
|
||||||
|
const builder = Builders.get(this.player.id);
|
||||||
|
builder.flexibleInstanceMovement = new FlexibleInstanceMove(this.instance, this.player);
|
||||||
|
}
|
||||||
|
|
||||||
setLayerForm() {
|
setLayerForm() {
|
||||||
InstanceFormBuilder.buildSetLayer(this.instance.getBounds().max.y, this.instance.getLayer()).show(this.player).then((response) => {
|
InstanceFormBuilder.buildSetLayer(this.instance.getBounds().max.y, this.instance.getLayer()).show(this.player).then((response) => {
|
||||||
if (response.canceled)
|
if (response.canceled)
|
||||||
|
|||||||
@@ -246,19 +246,6 @@ export class StructureInstance {
|
|||||||
this.setLayer(this.options.currentLayer - 1);
|
this.setLayer(this.options.currentLayer - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
startFlexibleMove(player) {
|
|
||||||
this.flexMovingPlayerId = player.id;
|
|
||||||
this.disable();
|
|
||||||
// create flex movement outliner
|
|
||||||
}
|
|
||||||
|
|
||||||
stopFlexibleMove(finalLocation) {
|
|
||||||
// disable flex movement outliner
|
|
||||||
this.move(this.getDimension(), finalLocation);
|
|
||||||
this.enable();
|
|
||||||
this.flexMovingPlayerId = void 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
isFlexibleMoving() {
|
isFlexibleMoving() {
|
||||||
return this.flexMovingPlayerId !== void 0;
|
return this.flexMovingPlayerId !== void 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { MolangVariableMap, system, world } from "@minecraft/server";
|
import { MolangVariableMap, system, TicksPerSecond, world } from "@minecraft/server";
|
||||||
import { Vector } from "../lib/Vector";
|
import { Vector } from "../lib/Vector";
|
||||||
|
|
||||||
export class Outliner {
|
export class Outliner {
|
||||||
@@ -6,15 +6,18 @@ export class Outliner {
|
|||||||
min = new Vector();
|
min = new Vector();
|
||||||
max = new Vector();
|
max = new Vector();
|
||||||
drawParticle = "construct:outline";
|
drawParticle = "construct:outline";
|
||||||
drawFrequency = 10;
|
drawFrequency;
|
||||||
|
particleLifetime;
|
||||||
|
|
||||||
#drawParticles = [];
|
#drawParticles = [];
|
||||||
#runner = void 0;
|
#runner = void 0;
|
||||||
|
|
||||||
constructor(dimension, min, max) {
|
constructor(dimension, min, max, drawFrequency = 10, particleLifetime = 20) {
|
||||||
this.dimension = dimension;
|
this.dimension = dimension;
|
||||||
this.min = Vector.from(min);
|
this.min = Vector.from(min);
|
||||||
this.max = Vector.from(max);
|
this.max = Vector.from(max);
|
||||||
|
this.drawFrequency = drawFrequency;
|
||||||
|
this.particleLifetime = particleLifetime;
|
||||||
this.vertices = this.getVertices(min, max);
|
this.vertices = this.getVertices(min, max);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -42,6 +45,8 @@ export class Outliner {
|
|||||||
for (const [particleType, location] of this.#drawParticles) {
|
for (const [particleType, location] of this.#drawParticles) {
|
||||||
const molang = new MolangVariableMap();
|
const molang = new MolangVariableMap();
|
||||||
molang.setColorRGBA("dot_color", colorCallback());
|
molang.setColorRGBA("dot_color", colorCallback());
|
||||||
|
const lifetimeSeconds = this.particleLifetime / TicksPerSecond;
|
||||||
|
molang.setFloat("lifetime", lifetimeSeconds);
|
||||||
try {
|
try {
|
||||||
this.dimension.spawnParticle(particleType, location, molang);
|
this.dimension.spawnParticle(particleType, location, molang);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
|||||||
@@ -0,0 +1,38 @@
|
|||||||
|
import { ButtonState, InputButton } from "@minecraft/server";
|
||||||
|
|
||||||
|
export class PlayerMovement {
|
||||||
|
inputInfo;
|
||||||
|
|
||||||
|
constructor(player) {
|
||||||
|
this.player = player;
|
||||||
|
this.inputInfo = player.inputInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
isJumping() {
|
||||||
|
return this.inputInfo.getButtonState(InputButton.Jump) === ButtonState.Pressed;
|
||||||
|
}
|
||||||
|
|
||||||
|
isSneaking() {
|
||||||
|
return this.inputInfo.getButtonState(InputButton.Sneak) === ButtonState.Pressed;
|
||||||
|
}
|
||||||
|
|
||||||
|
getMovementVector() {
|
||||||
|
return this.inputInfo.getMovementVector();
|
||||||
|
}
|
||||||
|
|
||||||
|
getMajorDirectionFacing() {
|
||||||
|
const { x, y, z } = this.player.getViewDirection();
|
||||||
|
const xzAngle = Math.atan2(z, x) * (180 / Math.PI);
|
||||||
|
if (y > 0.7)
|
||||||
|
return { x: 0, y: 1, z: 0 };
|
||||||
|
if (y < -0.7)
|
||||||
|
return { x: 0, y: -1, z: 0 };
|
||||||
|
if (xzAngle >= -45 && xzAngle < 45)
|
||||||
|
return { x: 1, y: 0, z: 0 };
|
||||||
|
else if (xzAngle >= 45 && xzAngle < 135)
|
||||||
|
return { x: 0, y: 0, z: 1 };
|
||||||
|
else if (xzAngle >= 135 || xzAngle < -135)
|
||||||
|
return { x: -1, y: 0, z: 0 };
|
||||||
|
return { x: 0, y: 0, z: -1 };
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,8 +3,9 @@ import { extension } from '../config';
|
|||||||
import { world, system, EntityComponentTypes, ItemStack, CommandPermissionLevel, CustomCommandStatus, Player } from '@minecraft/server';
|
import { world, system, EntityComponentTypes, ItemStack, CommandPermissionLevel, CustomCommandStatus, Player } from '@minecraft/server';
|
||||||
import { MenuForm } from '../classes/MenuForm';
|
import { MenuForm } from '../classes/MenuForm';
|
||||||
import { structureCollection } from '../classes/Structure/StructureCollection'
|
import { structureCollection } from '../classes/Structure/StructureCollection'
|
||||||
|
import { Builders } from '../classes/Builder/Builders';
|
||||||
|
|
||||||
const ACTION_ITEM = 'construct:menu';
|
export const MENU_ITEM = 'construct:menu';
|
||||||
|
|
||||||
const menuCmd = new Command({
|
const menuCmd = new Command({
|
||||||
name: 'construct',
|
name: 'construct',
|
||||||
@@ -29,7 +30,7 @@ function givePlayerConstructItem(origin) {
|
|||||||
if (player instanceof Player === false)
|
if (player instanceof Player === false)
|
||||||
return { status: CustomCommandStatus.Failure, message: 'This command can only be used by players.' };
|
return { status: CustomCommandStatus.Failure, message: 'This command can only be used by players.' };
|
||||||
system.run(() => {
|
system.run(() => {
|
||||||
const givenItemStack = player.getComponent(EntityComponentTypes.Inventory)?.container?.addItem(new ItemStack(ACTION_ITEM));
|
const givenItemStack = player.getComponent(EntityComponentTypes.Inventory)?.container?.addItem(new ItemStack(MENU_ITEM));
|
||||||
if (givenItemStack)
|
if (givenItemStack)
|
||||||
player.sendMessage('§cFailed to give you the Construct item.');
|
player.sendMessage('§cFailed to give you the Construct item.');
|
||||||
else
|
else
|
||||||
@@ -39,12 +40,17 @@ function givePlayerConstructItem(origin) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
world.beforeEvents.itemUse.subscribe((event) => {
|
world.beforeEvents.itemUse.subscribe((event) => {
|
||||||
if (!event.source || event.itemStack?.typeId !== ACTION_ITEM) return;
|
if (!event.source || event.itemStack?.typeId !== MENU_ITEM) return;
|
||||||
event.cancel = true;
|
event.cancel = true;
|
||||||
system.run(() => openMenu(event.source, event));
|
const builder = Builders.get(event.source.id);
|
||||||
|
system.run(() => {
|
||||||
|
if (builder.isFlexibleInstanceMoving())
|
||||||
|
return;
|
||||||
|
openMenu(event.source, event);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
function openMenu(sender, event = void 0) {
|
function openMenu(player, event = void 0) {
|
||||||
const options = { jumpToInstance: true }
|
const options = { jumpToInstance: true }
|
||||||
if (event) {
|
if (event) {
|
||||||
const instanceNames = structureCollection.getInstanceNames();
|
const instanceNames = structureCollection.getInstanceNames();
|
||||||
@@ -52,5 +58,5 @@ function openMenu(sender, event = void 0) {
|
|||||||
if (instanceNames.includes(instanceName))
|
if (instanceNames.includes(instanceName))
|
||||||
options.instanceName = instanceName;
|
options.instanceName = instanceName;
|
||||||
}
|
}
|
||||||
new MenuForm(sender, options);
|
new MenuForm(player, options);
|
||||||
}
|
}
|
||||||
@@ -17,7 +17,7 @@
|
|||||||
},
|
},
|
||||||
"minecraft:emitter_shape_point": {},
|
"minecraft:emitter_shape_point": {},
|
||||||
"minecraft:particle_lifetime_expression": {
|
"minecraft:particle_lifetime_expression": {
|
||||||
"max_lifetime": 1
|
"max_lifetime": "variable.lifetime"
|
||||||
},
|
},
|
||||||
"minecraft:particle_appearance_billboard": {
|
"minecraft:particle_appearance_billboard": {
|
||||||
"size": [0.2, 0.2],
|
"size": [0.2, 0.2],
|
||||||
|
|||||||
Reference in New Issue
Block a user