This commit is contained in:
2024-08-07 09:45:15 +02:00
commit 9d7b1e71ce
88 changed files with 21647 additions and 0 deletions

View File

@@ -0,0 +1,13 @@
import { Trigger } from "./trigger";
import { Maker } from "./maker";
import { Manager } from "./manager";
export * from "./trigger";
export * from "./maker";
export * from "./manager";
export const CCTrigger = {
Trigger,
Maker,
Manager,
};

View File

@@ -0,0 +1,204 @@
import { Trigger } from "./trigger";
import {
Block,
ChatSendAfterEvent,
PaletteColor,
EntityInventoryComponent,
ItemStack,
ItemUseOnBeforeEvent,
MolangVariableMap,
Player,
Vector3,
world,
} from "@minecraft/server";
import { vector3, vector3Distance } from "../utils/vectorUtils";
import { Mindkeeper, StoreType } from "../mindKeeper";
import { spawnParticle } from "../utils/particleUtils";
import { Manager } from "./manager";
export class Maker {
private manager: Manager;
log: Map<string, number> = new Map();
currentTrigger: Trigger | null = null;
waitingForPoint2: boolean = false;
point1: Vector3 = vector3(0, 0, 0);
point2: Vector3 = vector3(0, 0, 0);
mindKeeper: Mindkeeper;
constructor(mindKeeper: Mindkeeper, manager: Manager) {
this.mindKeeper = mindKeeper;
this.manager = manager;
}
OnChat(event: ChatSendAfterEvent) {
if (event.message === "!toggleTriggers") {
this.mindKeeper.set("ShowTriggers", (this.mindKeeper.get("ShowTriggers") as boolean) ? false : true);
}
if (event.message === "!TWand") {
const item = new ItemStack("minecraft:stick");
item.nameTag = "MakeTrigger";
((event.sender as Player).getComponent("inventory") as EntityInventoryComponent).container!.addItem(item);
world.sendMessage("Thou shall have the Powah");
}
if (event.message === "!deleteCurrentTrigger") {
let currentTriggerdTrigger = this.manager
.GetTriggers()
.filter((trigger) => trigger.IsPlayerInside(event.sender as Player));
if (currentTriggerdTrigger.length > 0) {
this.manager.GetTriggers().splice(this.manager.GetTriggers().indexOf(currentTriggerdTrigger[0]), 1);
world.sendMessage("Trigger deleted");
} else {
world.sendMessage("No trigger to delete");
}
this.manager.Save();
}
const command = event.message.split(" ")[0];
if (command == "!setFunction") {
const name = event.message.split(" ")[1];
const trigger = this.manager.GetCurrentActiveTriggers()[0];
if (trigger === undefined) {
world.sendMessage("No trigger selected");
return;
}
trigger.eventToDispatch = name;
this.manager.Save();
world.sendMessage(`Function set to ${name}`);
}
}
OnItemUse(event: ItemUseOnBeforeEvent) {
const currentItemHeld: ItemStack = event.itemStack;
const blockInteracted: Block = event.block;
const player: Player = event.source as Player;
const oldLog = this.log.get(player.name)!;
this.log.set(player.name, Date.now());
if (oldLog + 150 >= Date.now()) return;
if (currentItemHeld.typeId == "minecraft:stick" && currentItemHeld.nameTag == "MakeTrigger") {
if (this.waitingForPoint2) {
this.point2 = vector3(blockInteracted.location.x, blockInteracted.location.y, blockInteracted.location.z);
let minX = Math.min(this.point1.x, this.point2.x);
let maxX = Math.max(this.point1.x, this.point2.x) + 1;
let minY = Math.min(this.point1.y, this.point2.y);
let maxY = Math.max(this.point1.y, this.point2.y) + 1;
let minZ = Math.min(this.point1.z, this.point2.z);
let maxZ = Math.max(this.point1.z, this.point2.z) + 1;
//Take the outer bounds of the two points
this.point1 = vector3(minX, minY, minZ);
this.point2 = vector3(maxX, maxY, maxZ);
this.currentTrigger = new Trigger(this.point1, this.point2);
this.manager.AddTrigger(this.currentTrigger!);
this.currentTrigger = null;
this.waitingForPoint2 = false;
world.sendMessage(`Trigger added`);
this.manager.Save();
return;
} else {
this.point1 = vector3(event.block.location.x, event.block.location.y, event.block.location.z);
this.waitingForPoint2 = true;
world.sendMessage(`Select a second point`);
}
}
}
DrawLine(pos1: Vector3, pos2: Vector3, color: PaletteColor): void {
if (pos1 != null && pos2 != null) {
//draw a line with particles
const xStep = pos2.x - pos1.x;
const yStep = pos2.y - pos1.y;
const zStep = pos2.z - pos1.z;
const steps = Math.max(Math.abs(xStep), Math.abs(yStep), Math.abs(zStep));
for (let i = 0; i <= steps; i++) {
const x = pos1.x + (xStep / steps) * i;
const y = pos1.y + (yStep / steps) * i;
const z = pos1.z + (zStep / steps) * i;
//make the ends and starts a different color
let map = new MolangVariableMap();
if (i == 0 || i == steps) {
map.setColorRGB("variable.color", { red: 0, green: 255, blue: 0 });
const particleData: MolangVariableMap = new MolangVariableMap();
particleData.setColorRGB("variable.color", {
red: 0,
green: 1,
blue: 0,
});
spawnParticle(vector3(x, y + 0.1, z), "codecosmos:point", particleData);
} else {
map.setColorRGB("variable.color", { red: 0, green: 255, blue: 0 });
const particleData: MolangVariableMap = new MolangVariableMap();
particleData.setColorRGB("variable.color", {
red: 1,
green: 1,
blue: 1,
});
spawnParticle(vector3(x, y + 0.1, z), "codecosmos:point", particleData);
}
}
}
}
Update() {
let shouldRender = this.mindKeeper.get("ShowTriggers") as boolean;
this.manager.GetTriggers().forEach((trigger: Trigger) => {
trigger.Update();
if (shouldRender) {
const players = world.getAllPlayers();
const triggerCenter = vector3(
(trigger.point1.x + trigger.point2.x) / 2,
(trigger.point1.y + trigger.point2.y) / 2,
(trigger.point1.z + trigger.point2.z) / 2
);
const distnaces = players.map((player) => {
return {
player: player,
distance: vector3Distance(player.location, triggerCenter),
};
});
distnaces.sort((a, b) => a.distance - b.distance);
const closestPlayer = distnaces[0].player;
const distance = distnaces[0].distance;
if (distance > 25) return;
const p1 = trigger.point1;
const p2 = vector3(trigger.point1.x, trigger.point2.y, trigger.point1.z);
const p3 = vector3(trigger.point2.x, trigger.point1.y, trigger.point1.z);
const p4 = vector3(trigger.point2.x, trigger.point2.y, trigger.point1.z);
const p5 = vector3(trigger.point1.x, trigger.point1.y, trigger.point2.z);
const p6 = vector3(trigger.point1.x, trigger.point2.y, trigger.point2.z);
const p7 = vector3(trigger.point2.x, trigger.point1.y, trigger.point2.z);
const p8 = trigger.point2;
//is there a player inside this trigger
const color = trigger.IsAnyPlayerInside() ? PaletteColor.Red : PaletteColor.White;
this.DrawLine(p1, p2, color);
this.DrawLine(p2, p4, color);
this.DrawLine(p4, p3, color);
this.DrawLine(p3, p1, color);
this.DrawLine(p5, p6, color);
this.DrawLine(p6, p8, color);
this.DrawLine(p8, p7, color);
this.DrawLine(p7, p5, color);
this.DrawLine(p1, p5, color);
this.DrawLine(p2, p6, color);
this.DrawLine(p3, p7, color);
this.DrawLine(p4, p8, color);
}
});
}
}

View File

@@ -0,0 +1,117 @@
import { ChatSendAfterEvent, ItemUseOnBeforeEvent, Player, system, world } from "@minecraft/server";
import { Mindkeeper, StoreType } from "../mindKeeper";
import { vector3 } from "../utils/vectorUtils";
import { Maker } from "./maker";
import { Trigger } from "./trigger";
export class TriggerEvent {
player: Player;
trigger: Trigger;
constructor(player: Player, trigger: Trigger) {
this.player = player;
this.trigger = trigger;
}
}
export type TriggerEventHandler = (event: TriggerEvent) => void;
export class Manager {
private triggers: Trigger[] = [];
private functionTriggers: Map<string, TriggerEventHandler> = new Map();
private mindKeeper: Mindkeeper;
private maker: Maker;
constructor(mindKeeper: Mindkeeper) {
this.mindKeeper = mindKeeper;
this.maker = new Maker(mindKeeper, this);
}
RegisterStores() {
this.mindKeeper.registerStore("triggers", StoreType.string);
this.mindKeeper.registerStore("ShowTriggers", StoreType.boolean);
}
OnItemUse(event: ItemUseOnBeforeEvent) {
this.maker.OnItemUse(event);
}
OnChat(event: ChatSendAfterEvent) {
this.maker.OnChat(event);
}
//the func should have a TriggerEvent as a parameter
RegisterFunctionTrigger(name: string, func: TriggerEventHandler) {
this.functionTriggers.set(name, func);
}
//#region Loading / Saving
Load() {
this.LoadTriggers();
}
Save() {
this.SaveTriggers();
}
private LoadTriggers() {
const triggers = this.mindKeeper.get("triggers") as string;
if (triggers === undefined) return;
const data = JSON.parse(triggers);
data.forEach((trigger: any) => {
let point1 = vector3(trigger.point1.x, trigger.point1.y, trigger.point1.z);
let point2 = vector3(trigger.point2.x, trigger.point2.y, trigger.point2.z);
let eventToDispatch = trigger.eventToDispatch;
let newTrigger = new Trigger(point1, point2);
newTrigger.eventToDispatch = eventToDispatch;
this.triggers.push(newTrigger);
});
}
private SaveTriggers() {
this.mindKeeper.set("triggers", JSON.stringify(this.triggers));
}
//#endregion
AddTrigger(trigger: Trigger) {
this.triggers.push(trigger);
}
GetCurrentActiveTriggers(): Trigger[] {
return this.triggers.filter((trigger) => trigger.IsAnyPlayerInside());
}
Update() {
const players = world.getAllPlayers();
this.maker.Update();
this.triggers.forEach((trigger: Trigger) => {
trigger.Update();
players.forEach((player) => {
// player.sendMessage(`Player ${player.name} is in trigger ${trigger.IsPlayerInside(player)}`);
if (trigger.hasPlayerEnterdTrigger(player)) {
world.sendMessage(`Player ${player.name} entered trigger`);
//Check if a function trigger is set
const isFunctionSet = this.functionTriggers.has(trigger.eventToDispatch);
if (isFunctionSet) {
const event = new TriggerEvent(player, trigger);
this.functionTriggers.get(trigger.eventToDispatch)!(event);
} else {
world.sendMessage(`Triggered ${trigger.eventToDispatch}`);
}
}
});
});
}
GetTriggers(): Trigger[] {
return this.triggers;
}
}

View File

@@ -0,0 +1,82 @@
import { Player, Vector3, world } from "@minecraft/server";
import { vector3 } from "../utils/vectorUtils";
export class Trigger {
point1: Vector3 = vector3(0, 0, 0);
point2: Vector3 = vector3(0, 0, 0);
eventToDispatch: string = "";
isPlayerInTrigger: Map<string, boolean> = new Map();
hasTriggerd: boolean = false;
constructor(point1: Vector3, point2: Vector3) {
this.point1 = point1;
this.point2 = point2;
}
hasPlayerEnterdTrigger(player: Player): boolean {
const minX = Math.min(this.point1.x, this.point2.x);
const maxX = Math.max(this.point1.x, this.point2.x);
const minY = Math.min(this.point1.y, this.point2.y);
const maxY = Math.max(this.point1.y, this.point2.y);
const minZ = Math.min(this.point1.z, this.point2.z);
const maxZ = Math.max(this.point1.z, this.point2.z);
const playerPos = player.location;
const inside =
playerPos.x >= minX &&
playerPos.x <= maxX &&
playerPos.y >= minY &&
playerPos.y + 1 <= maxY &&
playerPos.z >= minZ &&
playerPos.z <= maxZ;
let entererdTrigger = false;
if (!this.isPlayerInTrigger.get(player.name) && inside) {
entererdTrigger = true;
}
this.isPlayerInTrigger.set(player.name, inside);
return entererdTrigger;
}
IsPlayerInside(player: Player): boolean {
return this.isPlayerInTrigger.get(player.name) || false;
}
IsAnyPlayerInside(): boolean {
let isInside = false;
world.getAllPlayers().forEach((player) => {
if (this.isPlayerInTrigger.get(player.name)) {
isInside = true;
}
});
return isInside;
}
ShouldTrigger(): boolean {
let isSomethingInTrigger = false;
const players = world.getAllPlayers();
players.forEach((player) => {
if (this.hasPlayerEnterdTrigger(player)) {
isSomethingInTrigger = true;
}
});
return isSomethingInTrigger;
}
Update() {
world
.getDimension("overworld")
.getEntities({ type: "minecraft:player" })
.forEach((player) => {
//Check if the distance between the player and the trigger is less than the width of the trigger
// if (this.hasPlayerEnterdTrigger(player.location)) {
// world.sendMessage(`Player ${player.nameTag} is in trigger`);
// }
});
}
}

View File

@@ -0,0 +1,70 @@
class Chalk {
static red(text: string): string {
return "§c" + text + "§r";
}
static yellow(text: string): string {
return "§e" + text + "§r";
}
static green(text: string): string {
return "§a" + text + "§r";
}
static blue(text: string): string {
return "§9" + text + "§r";
}
static aqua(text: string): string {
return "§b" + text + "§r";
}
static white(text: string): string {
return "§f" + text + "§r";
}
static black(text: string): string {
return "§0" + text + "§r";
}
static gold(text: string): string {
return "§6" + text + "§r";
}
static gray(text: string): string {
return "§7" + text + "§r";
}
static darkRed(text: string): string {
return "§4" + text + "§r";
}
static darkGreen(text: string): string {
return "§2" + text + "§r";
}
static darkBlue(text: string): string {
return "§1" + text + "§r";
}
static darkAqua(text: string): string {
return "§3" + text + "§r";
}
static darkPurple(text: string): string {
return "§5" + text + "§r";
}
static darkGray(text: string): string {
return "§8" + text + "§r";
}
static lightPurple(text: string): string {
return "§d" + text + "§r";
}
static bold(text: string): string {
return "§l" + text + "§r";
}
static italic(text: string): string {
return "§o" + text + "§r";
}
static underline(text: string): string {
return "§n" + text + "§r";
}
static strikethrough(text: string): string {
return "§m" + text + "§r";
}
static obfuscated(text: string): string {
return "§k" + text + "§r";
}
static reset(text: string): string {
return "§r" + text + "§r";
}
}
export default Chalk;

View File

@@ -0,0 +1,99 @@
import { CommandResult, Dimension, Entity, world, Player } from "@minecraft/server";
export class Command {
private __player: Player;
public argv: IterableIterator<string>;
public get player(): Player {
return this.__player;
}
public get argv0(): string {
return this.argv.next().value;
}
constructor(argv: string[], player: Player) {
this.argv = (function* () {
for (let arg of argv) yield arg;
})();
this.__player = player;
}
}
export class Commands {
/**
* @remarks
* Runs a particular command synchronously from the context.
* @param commandString
* Command to run. Note that command strings should not start
* with slash.
* @param target
* Target to be used as context for the command to run
* within.
* @returns
* For commands that return data, returns a CommandResult with
* an indicator of command results.
* @throws This function can throw errors.
*/
static run(commandString: string, target: Dimension | Entity = world.getDimension("overworld")): CommandResult {
if (target instanceof Dimension || Entity) return target.runCommand(commandString);
else throw TypeError("Native type conversion failed");
}
/**
* @remarks
* Runs a particular command asynchronously from the context.
* Where possible - and especially for
* long-running operations - you should use runCommandAsync
* over runCommand.
* @param commandString
* Command to run. Note that command strings should not start
* with slash.
* @param target
* Target to be used as context for the command to run
* within.
* @returns
* For commands that return data, returns a CommandResult with
* an indicator of command results.
* @throws This function can throw errors.
*/
static async runAsync(
commandString: string,
target: Dimension | Entity = world.getDimension("overworld")
): Promise<CommandResult> {
if (target instanceof Dimension || Entity) return await target.runCommandAsync(commandString);
else throw TypeError("Native type conversion failed");
}
/**
* @remarks
* Registers a new custom command. This command will become
* available in Minecraft via [prefix][command].
* @param prefix
* The prefix of this specific command. (Case sensitive)
* @param command
* Name of this specific command. (Case sensitive)
* @param commandFunction
* Implementation of the command function.
* @throws
* This function can throw error: You are not allow to register a new slash command.
* @example example1.js
* ```typescript
* Commands.register("!", "test", function (arg) {
* arg.player.runCommandAsync(`say ${arg.argv0} ${JSON.stringify([...arg.argv])}`);
* });
* ```
*/
public static register(prefix: string, command: string, commandFunction: (arg: Command) => void): void {
if (prefix.startsWith("/")) throw Error("Unable to register slash commands.");
world.beforeEvents.chatSend.subscribe((arg) => {
var argv = arg.message.split(/(".*?"|[^"\s]+)+(?=\s*|\s*$)/g).filter((e) => e.trim().length > 0);
if (argv[0] === `${prefix}${command}`) {
arg.cancel = true;
try {
commandFunction(new Command(argv, arg.sender));
} catch (err) {
let { statusMessage } = JSON.parse(err as string);
console.error(err);
arg.sender.sendMessage(`§c${statusMessage}`);
}
}
});
}
}

View File

@@ -0,0 +1,9 @@
abstract class AbstractLevelCondition {
constructor() {}
abstract checkCondition(): boolean;
}
export default AbstractLevelCondition;
//Interface could be better

View File

@@ -0,0 +1,24 @@
import { BlockType, Vector3, world } from "@minecraft/server";
import AbstractLevelCondition from "./AbstractCondition";
import { Vector3ToString } from "../utils/vectorUtils";
class BlockCondition extends AbstractLevelCondition {
position: Vector3;
blockType: BlockType;
constructor(position: Vector3, blockType: BlockType) {
super();
this.position = position;
this.blockType = blockType;
}
checkCondition(): boolean {
const block = world.getDimension("overworld").getBlock(this.position);
if (!block) {
return false;
}
return block.typeId === this.blockType.id;
}
}
export default BlockCondition;

View File

@@ -0,0 +1,21 @@
import { BlockType, Vector3, world } from "@minecraft/server";
import AbstractLevelCondition from "./AbstractCondition";
class ButtonPushCondition extends AbstractLevelCondition {
position: Vector3;
constructor(position: Vector3) {
super();
this.position = position;
}
checkCondition(): boolean {
const button = world.getDimension("overworld").getBlock(this.position);
if (!button || !button.getRedstonePower()) {
return false;
}
return button.getRedstonePower()! > 0;
}
}
export default ButtonPushCondition;

View File

@@ -0,0 +1,52 @@
import { World } from "@minecraft/server";
class Level {
levelCompleteCallback: Function;
levelCheckCallback: Function;
levelSetupCallback: Function;
levelUpdateCallback: Function;
levelResetCallback: Function;
isCompleted: boolean = false;
isSetup: boolean = false;
constructor(
levelSetupCallback: Function,
levelUpdateCallback: Function,
levelCompleteCallback: Function,
levelCheckCallback: Function,
levelResetCallback: Function = () => {}
) {
this.levelSetupCallback = levelSetupCallback;
this.levelCompleteCallback = levelCompleteCallback;
this.levelCheckCallback = levelCheckCallback;
this.levelUpdateCallback = levelUpdateCallback;
this.levelResetCallback = levelResetCallback;
}
setup() {
this.levelSetupCallback();
}
update() {
if (!this.isSetup) {
this.setup();
this.isSetup = true;
}
this.levelUpdateCallback();
if (this.levelCheckCallback() && !this.isCompleted) {
this.levelCompleteCallback();
this.isCompleted = true;
}
}
reset() {
this.levelResetCallback();
this.isCompleted = false;
}
}
//nextlevel
//mindkeeper
//pupeteer
export default Level;

View File

@@ -0,0 +1,44 @@
import { BlockType, Vector3, World } from "@minecraft/server";
export type blockCondition = {
block: string;
position: Vector3;
};
/**
* Checks if there is a lever at the specified position in the world.
*
* @param world - The world object.
* @param position - The position of the lever.
* @returns Returns true if there is a lever at the specified position and it has a redstone power of 15, otherwise returns false.
* @throws Throws an error if there is no lever at the specified position.
*/
export function leverOn(world: World, position: Vector3): boolean {
let lever = world.getDimension("overworld").getBlock(position);
if (!(lever?.typeId == "minecraft:lever")) {
throw new Error(`No lever at ${position}`);
}
return lever.getRedstonePower() == 15;
}
export type LeverCondition = {
position: Vector3;
state: boolean;
};
export type LevelBlockCondition = {
conditions: blockCondition[];
};
export type LevelLeverCondition = {
conditions: LeverCondition[];
};
export type AgentNoGoZone = {
//Reason for needing to use is because the Agent can't be queried for its location in minecraft
position: Vector3;
};
export type LevelNoGoZone = {
zones: AgentNoGoZone[];
};

View File

@@ -0,0 +1,230 @@
import { ChatSendAfterEvent, Vector3, World, system } from "@minecraft/server";
class Store {
type: StoreType;
name: string;
constructor(type: StoreType, name: string) {
this.type = type;
this.name = name;
}
getType(): StoreType {
return this.type;
}
getName(): string {
return this.name;
}
}
enum StoreType {
string = "string",
number = "number",
boolean = "boolean",
}
/**
* Represents a Mindkeeper, responsible for managing stores and dynamic properties in a world.
*/
class Mindkeeper {
registerdStores: Array<Store> = [];
// propertyManager = new DynamicPropertiesDefinition();
world: World;
initialised: boolean = false;
debugLog: string[] = [];
/**
* Creates a new instance of Mindkeeper.
* @param world The world associated with the Mindkeeper.
*/
constructor(world: World) {
this.world = world;
}
/**
* Registers a store with the specified name and type.
* @param store The name of the store.
* @param type The type of the store.
*/
registerStore(store: string, type: StoreType): void {
this.registerdStores.push(new Store(type, store));
}
/**
* Returns an array of registered stores.
* @returns An array of registered stores.
*/
getStores() {
return this.registerdStores;
}
/**
* Prints the debug log by sending each log entry as a message to the world.
*/
printDebug() {
this.debugLog.forEach((t) => {
this.world.sendMessage(t);
});
}
/**
* Registers the dynamic properties to the world's property registry.
* @param propertyRegistry The property registry of the world.
*/
registerToWorld() {
for (let i = 0; i < this.registerdStores.length; i++) {
let isAlreadyDefined = true;
try {
let test = this.world.getDynamicProperty(this.registerdStores[i].getName());
if (test === undefined) {
isAlreadyDefined = false;
}
} catch (e) {
isAlreadyDefined = false;
}
if (isAlreadyDefined) {
continue;
}
switch (this.registerdStores[i].getType()) {
case StoreType.string:
this.world.setDynamicProperty(this.registerdStores[i].getName(), "");
this.debugLog.push("registerd string" + this.registerdStores[i].getName());
break;
case StoreType.number:
this.world.setDynamicProperty(this.registerdStores[i].getName(), 0);
this.debugLog.push("registerd number" + this.registerdStores[i].getName());
break;
case StoreType.boolean:
this.world.setDynamicProperty(this.registerdStores[i].getName(), false);
this.debugLog.push("registerd boolean" + this.registerdStores[i].getName());
break;
}
}
this.initialised = true;
}
/**
* Sets the value of a store.
* @param store The name of the store.
* @param value The value to set.
*/
set(store: string, value: string | number | boolean | Vector3): void {
if (this.registerdStores.find((s) => s.getName() === store)?.getType() != typeof value) {
this.world.sendMessage(`Store ${store} is not of type ${typeof value}`);
return;
}
system.run(() => {
this.world.setDynamicProperty(store, value);
});
}
/**
* Retrieves the value of a store.
* @param store The name of the store.
* @returns The value of the store, or undefined if the store is not defined.
*/
get(store: string): string | number | boolean | Vector3 | undefined {
try {
let data = this.world.getDynamicProperty(store);
if (data === undefined) {
this.world.sendMessage(`Store ${store} is not defined`);
return undefined;
}
return data;
} catch (e) {
// this.world.sendMessage(`Store ${store} is not defined`);
return undefined;
}
}
/**
* Increments the value of a store if it is a number.
* @param store The name of the store.
*/
increment(store: string): void {
let data = this.get(store);
if (typeof data === "number") {
this.set(store, data + 1);
}
}
/**
* Handles chat commands by executing the corresponding actions.
* @param event The chat send after event.
*/
private secondWarning = false;
chatCommands(event: ChatSendAfterEvent) {
const command = event.message.split(" ")[0];
const args = event.message.split(" ").slice(1);
if (command === "!get") {
const store = event.message.split(" ")[1];
const value = this.get(store);
this.world.sendMessage(`Value of ${store} is ${value}`);
}
if (command === "!set") {
const store = event.message.split(" ")[1];
if (store === undefined) {
this.world.sendMessage(`Please provide a store to set`);
return;
}
const value = event.message.split(" ")[2];
if (value === undefined) {
this.world.sendMessage(`Please provide a value to set for ${store}`);
return;
}
const type = event.message.split(" ")[3];
let actualType = this.getStores()
.find((s) => s.getName() === store)
?.getType();
if (actualType === undefined) {
this.world.sendMessage(`Store ${store} is not defined`);
return;
}
switch (actualType) {
case StoreType.string:
this.set(store, String(value));
break;
case StoreType.number:
if (isNaN(Number(value))) {
this.world.sendMessage(`Can't parse ${value} as a number`);
return;
}
this.set(store, Number(value));
break;
case StoreType.boolean:
const ActualValue = value.toLowerCase();
this.set(store, ActualValue === "true");
break;
}
this.world.sendMessage(`Value of ${store} is ${value}`);
}
if (event.message.startsWith("!listStores")) {
this.getStores().forEach((store) => {
this.world.sendMessage(`${store.getName()} is ${store.getType()}`);
});
}
if (command === "!deleteStores") {
this.world.sendMessage("ARE YOU SURE YOU WANT TO DELETE ALL STORES? THIS COULD CAUSE ISSUES");
this.world.sendMessage("If you are sure, type !deleteStoresConfirm");
this.secondWarning = true;
}
if (this.secondWarning) {
if (command === "!deleteStoresConfirm") {
this.getStores().forEach((store) => {
this.world.sendMessage(`Deleting ${store.getName()}`);
this.world.sendMessage("This feature no longer works, thanks minecraft ");
// this.world.dynami(store.getName());
});
this.secondWarning = false;
}
}
}
}
export { Mindkeeper, Store, StoreType };

View File

@@ -0,0 +1,16 @@
class NextLevel {
currentState = 0;
states: Array<Function> = [];
constructor(states: Array<Function>) {
this.states = states;
}
update() {
this.states[this.currentState]();
}
next() {
this.currentState++;
}
}

View File

@@ -0,0 +1,112 @@
import {
EntityInventoryComponent,
ItemStack,
Player,
RawText,
TicksPerSecond,
TitleDisplayOptions,
Vector3,
World,
system,
world,
} from "@minecraft/server";
import { delayedRun } from "./utils/waitUtil";
class Pupeteer {
private static getActualString(message: string): string | RawText {
if (message.startsWith("%")) {
const key = message.substring(1);
return { rawtext: [{ translate: key }] };
}
return message;
}
static setActionBarTimed(message: string, duration: number): void {
world.getPlayers().forEach((player) => {
system.run(() => {
player.onScreenDisplay.setActionBar(this.getActualString(message));
});
});
delayedRun(() => {
this.clearActionBar();
}, duration * TicksPerSecond);
}
static setActionBar(message: string): void {
world.getPlayers().forEach((player) => {
system.run(() => {
player.onScreenDisplay.setActionBar(this.getActualString(message));
});
});
}
static sendWorldMessage(message: string): void {
world.sendMessage(this.getActualString(message));
}
static setTitleTimed(message: string, duration: number): void {
world.getPlayers().forEach((player) => {
let options: TitleDisplayOptions = {
fadeInDuration: 20,
fadeOutDuration: 20,
stayDuration: duration * TicksPerSecond,
};
player.onScreenDisplay.setTitle(this.getActualString(message), options);
});
}
static setTitle(message: string): void {
world.getPlayers().forEach((player) => {
player.onScreenDisplay.setTitle(message);
});
}
static updateSubtitle(message: string): void {
world.getPlayers().forEach((player) => {
player.onScreenDisplay.updateSubtitle(this.getActualString(message));
});
}
static clearTitle(): void {
world.getPlayers().forEach((player) => {
player.onScreenDisplay.setTitle("");
});
}
static clearSubtitle(): void {
world.getPlayers().forEach((player) => {
player.onScreenDisplay.updateSubtitle("");
});
}
static clearActionBar(): void {
world.getPlayers().forEach((player) => {
player.onScreenDisplay.setActionBar("");
});
}
static testForLocation(location: Vector3, radius: number): boolean {
let isPlayerInArea = false;
world.getPlayers().forEach((player) => {
let dx = location.x - player.location.x;
let dy = location.y - player.location.y;
let dz = location.z - player.location.z;
let distance = Math.sqrt(dx * dx + dy * dy + dz * dz);
if (distance < radius) {
isPlayerInArea = true;
}
});
return isPlayerInArea;
}
static setNpcText(npcTag: string, sceneName: string) {
world.getDimension("overworld").runCommand(`/dialogue change @e[tag=${npcTag}] ${sceneName} @a`);
}
static givePlayerItem(player: Player, item: ItemStack) {
(player.getComponent("inventory") as EntityInventoryComponent).container!.addItem(item);
}
}
export default Pupeteer;

View File

@@ -0,0 +1,85 @@
import { MolangVariableMap, Vector3, world } from "@minecraft/server";
import { Vector3Add } from "../utils/vectorUtils";
import { TrailType } from "./trailTypes";
class TrailPoint {
postion: Vector3;
index: number;
constructor(position: Vector3, index: number) {
this.postion = position;
this.index = index;
}
spawn() {
// let spawnPosition: Vector3 = Vector3Add(this.postion, { x: 0.5, y: 0.5, z: 0.5 });
let spawnPosition: Vector3 = this.postion;
try {
world
.getDimension("overworld")
.spawnParticle("minecraft:balloon_gas_particle", spawnPosition, new MolangVariableMap());
} catch (e) {}
}
}
class Trail {
id: string;
points: TrailPoint[] = [];
currentPoint: number = 0;
wrapIndex: number = 0;
nextParticleTimer: number = 0;
currentParticleCounter: number = 0;
calculatedLength: number = 0;
constructor(id: string, nextParticleTimer: number = 5, wrapIndex: number = 0) {
this.id = id;
this.nextParticleTimer = nextParticleTimer;
this.wrapIndex = wrapIndex;
}
addPoint(point: TrailPoint) {
this.points.push(point);
//this could be a one liner,
let maxlength: number = 0;
this.points.forEach((point) => {
if (point.index > maxlength) {
maxlength = point.index;
}
});
this.calculatedLength = maxlength;
}
fromTrail(trail: TrailType) {
trail.points.forEach((point) => {
this.addPoint(new TrailPoint(point.position, point.index));
});
}
spawnNext() {
if (this.currentParticleCounter >= this.nextParticleTimer) {
this.currentParticleCounter = 0;
//wrapindex is in how many segments the trail is divided into
let pointsPerInterval = this.wrapIndex > 0 ? this.calculatedLength / this.wrapIndex : 0;
this.points
.filter((point) => {
return (
point.index === this.currentPoint ||
(this.wrapIndex > 0 && point.index % pointsPerInterval === this.currentPoint)
);
})
.forEach((point) => {
point.spawn();
});
this.currentPoint++;
if (this.currentPoint >= this.calculatedLength) {
this.currentPoint = 0;
}
} else {
this.currentParticleCounter++;
}
}
}
export { Trail, TrailPoint };

View File

@@ -0,0 +1,212 @@
import {
Block,
ChatSendAfterEvent,
EntityInventoryComponent,
ItemStack,
ItemUseOnBeforeEvent,
MolangVariableMap,
Player,
world,
} from "@minecraft/server";
import { Vector3 } from "@minecraft/server";
import { Vector3Add, Vector3ToString, vector3 } from "../utils/vectorUtils";
import { spawnParticle } from "../utils/particleUtils";
import Pupeteer from "../pupeteer";
import Chalk from "../chalk";
export namespace TrailMaker {
export class Maker {
currentTrail: Trail;
log: Map<string, number> = new Map();
selectedIndex: number = 0;
//Line
point1: Vector3 = vector3(0, 0, 0);
point2: Vector3 = vector3(0, 0, 0);
waitingForPoint2: boolean = false;
OnChat(event: ChatSendAfterEvent) {
if (event.message == "!trailWand") {
let item: ItemStack = new ItemStack("minecraft:stick");
item.nameTag = "AddPoint";
Pupeteer.givePlayerItem(event.sender as Player, item);
world.sendMessage("Thou shall have the Trailing Powah");
}
if (event.message == "!trailDeleteWand") {
let item: ItemStack = new ItemStack("minecraft:stick");
item.nameTag = "DeletePoint";
Pupeteer.givePlayerItem(event.sender as Player, item);
world.sendMessage("Luke, i'm NOT your father");
}
if (event.message == "!trailLineWand") {
let item: ItemStack = new ItemStack("minecraft:stick");
item.nameTag = "AddLine";
Pupeteer.givePlayerItem(event.sender as Player, item);
world.sendMessage(`This is where i draw the ${Chalk.red("Line")} >:(`);
}
if (event.message == "!trailExport") {
this.Export();
}
}
OnItemUse(event: ItemUseOnBeforeEvent) {
const currentItemHeld: ItemStack = event.itemStack;
const blockInteracted: Block = event.block;
const player: Player = event.source as Player;
const oldLog = this.log.get(player.name)!;
this.log.set(player.name, Date.now());
if (oldLog + 150 >= Date.now()) return;
if (event.itemStack.typeId == "minecraft:stick" && event.itemStack.nameTag == "AddPoint") {
let block = event.block;
let pos = this.BlockToParticlePosition(block);
world.sendMessage(`Added Point ${Vector3ToString(pos)}`);
this.currentTrail.points.push(new Point(pos, this.currentTrail.currentPoint));
this.currentTrail.currentPoint++;
Pupeteer.setActionBarTimed(`Added point ${this.currentTrail.currentPoint}`, 3);
}
if (event.itemStack.typeId == "minecraft:stick" && event.itemStack.nameTag == "DeletePoint") {
let block = event.block;
let pos = this.BlockToParticlePosition(block);
let point = this.currentTrail.points.find((point) => Vector3ToString(point.position) == Vector3ToString(pos));
if (point) {
this.currentTrail.points.splice(this.currentTrail.points.indexOf(point), 1);
Pupeteer.setActionBarTimed(`Deleted point ${point.index}`, 3);
} else {
Pupeteer.setActionBarTimed(`No point found`, 3);
}
}
if (event.itemStack.typeId == "minecraft:stick" && event.itemStack.nameTag == "AddLine") {
world.sendMessage(`Waiting for point 2: ${this.waitingForPoint2}`);
if (!this.waitingForPoint2) {
// this.point1 = vector3(blockInteracted.location.x, blockInteracted.location.y, blockInteracted.location.z);
this.point1 = this.BlockToParticlePosition(blockInteracted);
this.waitingForPoint2 = true;
Pupeteer.setActionBar("Select a second point");
world.sendMessage("Select a second point");
return;
} else {
this.point2 = this.BlockToParticlePosition(blockInteracted);
//Calculate the blocks between these 2 points, andd add them to the trail
//Assume point1 is the start, and point2 is the end
let x1 = this.point1.x;
let x2 = this.point2.x;
let y1 = this.point1.y;
let y2 = this.point2.y;
let z1 = this.point1.z;
let z2 = this.point2.z;
//Find out what axis is the movement on, throw an error if it's on more than one axis
let xDiff = Math.abs(x2 - x1);
let yDiff = Math.abs(y2 - y1);
let zDiff = Math.abs(z2 - z1);
let axis = "";
if (xDiff > 0 && yDiff == 0 && zDiff == 0) {
axis = "x";
} else if (xDiff == 0 && yDiff > 0 && zDiff == 0) {
axis = "y";
} else if (xDiff == 0 && yDiff == 0 && zDiff > 0) {
axis = "z";
} else {
Pupeteer.setActionBarTimed("Invalid line", 3);
return;
}
let start = 0;
let end = 0;
if (axis == "x") {
start = Math.min(x1, x2);
end = Math.max(x1, x2);
} else if (axis == "y") {
start = Math.min(y1, y2);
end = Math.max(y1, y2);
} else if (axis == "z") {
start = Math.min(z1, z2);
end = Math.max(z1, z2);
}
for (let i = start; i <= end; i++) {
let pos = vector3(0, 0, 0);
if (axis == "x") {
pos = vector3(i, y1, z1);
} else if (axis == "y") {
pos = vector3(x1, i, z1);
} else if (axis == "z") {
pos = vector3(x1, y1, i);
}
this.currentTrail.points.push(new Point(pos, this.currentTrail.currentPoint));
this.currentTrail.currentPoint++;
}
this.waitingForPoint2 = false;
Pupeteer.setActionBarTimed("Added line", 3);
}
}
}
Update() {
this.currentTrail.points.forEach((point) => {
spawnParticle(point.position, "minecraft:balloon_gas_particle", new MolangVariableMap());
});
}
Export() {
let output = "";
for (let i = 0; i < this.currentTrail.points.length; i++) {
let point = this.currentTrail.points[i];
let actualPos = point.position;
output += `{ index: ${point.index}, position: vector3(${actualPos.x}, ${actualPos.y}, ${actualPos.z}) },\n`;
}
console.warn(output);
}
private BlockToParticlePosition(block: Block): Vector3 {
let pos = vector3(block.location.x, block.location.y, block.location.z);
pos = Vector3Add(pos, vector3(0.5, 0, 0.5));
//If block is a slab or stair, offset by half a block
let isHalfBlock = block.typeId.includes("slab") || block.typeId.includes("stair");
if (isHalfBlock) {
pos = Vector3Add(pos, vector3(0, 0.5, 0));
} else {
pos = Vector3Add(pos, vector3(0, 1.1, 0));
}
return pos;
}
constructor() {
this.currentTrail = new Trail();
}
}
class Trail {
points: Point[] = [];
currentPoint: number = 0;
nextParticleTimer: number = 0;
currentParticleCounter: number = 0;
constructor() {}
}
class Point {
position: Vector3;
index: number;
constructor(position: Vector3, index: number) {
this.position = position;
this.index = index;
}
}
}

View File

@@ -0,0 +1,13 @@
import { Vector3 } from "@minecraft/server";
type TrailPointType = {
index: number;
position: Vector3;
};
type TrailType = {
name: string;
points: TrailPointType[];
};
export { TrailPointType, TrailType };

View File

@@ -0,0 +1,40 @@
import { Entity, Vector3, world } from "@minecraft/server";
import { Vector3ToString, vector3 } from "./vectorUtils";
import { mindKeeper } from "../../main";
function teleportAgent(position: Vector3) {
world
.getDimension("overworld")
.runCommand(`/execute as @a run tp @e[type=agent] ${position.x} ${position.y} ${position.z}`);
}
function isAgentAt(position: Vector3): boolean {
let isAgentAt: boolean = false;
world
.getDimension("overworld")
.getEntitiesAtBlockLocation(position)
.forEach((entity) => {
if (!entity.isValid()) {
world.sendMessage("INVALID ENTITY");
return;
}
if (entity.typeId == "minecraft:agent") {
isAgentAt = true;
}
});
return isAgentAt;
}
function getAgent(): Entity {
let agent = world.getEntity(mindKeeper.get("agentid") as string);
return agent!;
}
function getAgentLocation(): Vector3 {
let agentLocation: Vector3 = vector3(0, 0, 0);
let agent = world.getEntity(mindKeeper.get("agentid") as string);
agentLocation = agent!.location;
return agentLocation;
}
export { teleportAgent, isAgentAt, getAgentLocation, getAgent };

View File

@@ -0,0 +1,62 @@
import { MolangVariableMap, Vector3, world } from "@minecraft/server";
import { Vector3Add, vector3 } from "./vectorUtils";
import { PARTICLES, spawnParticle } from "./particleUtils";
let arrowTemplate: Vector3[] = [
vector3(0, 0, 0),
vector3(1, 1, 0),
vector3(-1, 1, 0),
vector3(1.5, 2, 0),
vector3(-1.5, 2, 0),
vector3(2, 3, 0),
vector3(-2, 3, 0),
vector3(0, 1, 0),
vector3(0, 2, 0),
vector3(0, 3, 0),
vector3(0, 4, 0),
vector3(0, 5, 0),
];
let offset: Vector3 = vector3(0, 0, 0);
let angleOffset = 0;
let heightBobbing = 0;
let tickCounter = 0;
function rotate(pos: Vector3, angle: number) {
let x = pos.x;
let z = pos.z;
let newX = x * Math.cos(angle) - z * Math.sin(angle);
let newZ = x * Math.sin(angle) + z * Math.cos(angle);
return vector3(newX, pos.y, newZ);
}
const particleData = new MolangVariableMap();
particleData.setColorRGB("variable.color", {
red: 1,
green: 1,
blue: 1,
});
function drawArrow(offsetPos: Vector3) {
tickCounter++;
if (tickCounter % 2 == 0) {
angleOffset += 0.1;
heightBobbing += 0.3;
offset = vector3(46.5, 75, 220.5);
arrowTemplate.forEach((pos) => {
let rotatedPos = rotate(pos, angleOffset);
let finalPos = Vector3Add(offsetPos, rotatedPos);
finalPos.y += Math.sin(heightBobbing) / 2;
spawnParticle(finalPos, PARTICLES.point, particleData);
});
}
}
export { drawArrow };

View File

@@ -0,0 +1,24 @@
import { BlockType, BlockTypes, BlockVolume, Vector3, world } from "@minecraft/server";
import { MinecraftBlockTypes } from "../../vanilla-data/mojang-block";
type Wall = {
startPos: Vector3;
endPos: Vector3;
};
function clearWall(wall: Wall) {
let volume: BlockVolume = new BlockVolume(wall.startPos, wall.endPos);
world.getDimension("overworld").fillBlocks(volume, MinecraftBlockTypes.Air);
}
function fillWall(wall: Wall, block: BlockType) {
let volume: BlockVolume = new BlockVolume(wall.startPos, wall.endPos);
world.getDimension("overworld").fillBlocks(volume, block);
}
function startLevel(commandBlockPos: Vector3) {
let volume: BlockVolume = new BlockVolume(commandBlockPos, commandBlockPos);
world.getDimension("overworld").fillBlocks(volume, MinecraftBlockTypes.RedstoneBlock);
}
BlockTypes;
export { Wall, clearWall, fillWall, startLevel };

View File

@@ -0,0 +1,232 @@
import { MolangVariableMap, Vector3, world } from "@minecraft/server";
import { Vector3Add, vector3 } from "./vectorUtils";
const bedrockParticles = [
"minecraft:mobspell_emitter",
"minecraft:villager_angry",
"minecraft:bleach",
"minecraft:breaking_item_icon",
"minecraft:blockdust",
"minecraft:bubble_column_up_particle",
"minecraft:compfire_smoke_particle",
"minecraft:campfire_tall_smoke_particle",
"minecraft:cherry_leaves_particle",
"minecraft:crop_growth_emitter",
"minecraft:conduit_particle",
"minecraft:critical_hit_emitter",
"minecraft:bubble_column_down_particle",
"minecraft:dolphin_move_particle",
"minecraft:dragon_breath_trail",
"minecraft:dragon_breath_lingering",
"minecraft:lava_drip_particle",
"minecraft:water_drip_particle",
"minecraft:redstone_wire_dust_particle",
"minecraft:sculk_sensor_redstone_particle",
"minecraft:splash_spell_emitter",
"minecraft:electric_spark__particle",
"minecraft:enchanting_table_particle",
"minecraft:endrod",
"minecraft:balloon_gas_particle",
"minecraft:evoker_spell",
"minecraft:huge_explosion_emitter",
"minecraft:falling_dust_red_sand_particle",
"minecraft:falling_dust_sand_particle",
"minecraft:falling_dust_gravel_particle",
"minecraft:falling_dust_top_snow_particle",
"minecraft:falling_dust_dragon_egg_particle",
"minecraft:falling_dust_concrete_particle",
"minecraft:falling_dust_scaffolding_particle",
"minecraft:honey_drip_particle",
"minecraft:nectar_drip_particle",
"minecraft:obsidian_tear_particle",
"minecraft:spore_blossom_shower_particle",
"minecraft:water_splash_particle",
"minecraft:sparkler_emitter",
"minecraft:water_wake_particle",
"minecraft:basic_flame_particle",
"minecraft:flash",
"minecraft:glow_particle",
"minecraft:villager_happy",
"minecraft:heart_particle",
"minecraft:water_evaporation_actor_emitter",
"minecraft:lava_particle",
"minecraft:mobflame_emitter",
"minecraft:mobflame_single",
"minecraft:mycelium_dust_particle",
"minecraft:note_particle",
"minecraft:explode",
"minecraft:mob_portal",
"minecraft:rainsplash",
"minecraft:basic_smoke_particle",
"minecraft:snowflake_particle",
"minecraft:soul_particle",
"minecraft:blue_flame_particle",
"minecraft:spore_blossom_ambient_particle",
"minecraft:watersplash",
"minecraft:terrain",
"minecraft:totem_particle",
"minecraft:tracking_emitter",
"minecraft:vibration_signal",
"minecraft:wax_particle",
"minecraft:witchspell",
];
const PARTICLES = {
mobspell_emitter: "minecraft:mobspell_emitter",
villager_angry: "minecraft:villager_angry",
bleach: "minecraft:bleach",
breaking_item_icon: "minecraft:breaking_item_icon",
blockdust: "minecraft:blockdust",
bubble_column_up_particle: "minecraft:bubble_column_up_particle",
compfire_smoke_particle: "minecraft:compfire_smoke_particle",
campfire_tall_smoke_particle: "minecraft:campfire_tall_smoke_particle",
cherry_leaves_particle: "minecraft:cherry_leaves_particle",
crop_growth_emitter: "minecraft:crop_growth_emitter",
conduit_particle: "minecraft:conduit_particle",
critical_hit_emitter: "minecraft:critical_hit_emitter",
bubble_column_down_particle: "minecraft:bubble_column_down_particle",
dolphin_move_particle: "minecraft:dolphin_move_particle",
dragon_breath_trail: "minecraft:dragon_breath_trail",
dragon_breath_lingering: "minecraft:dragon_breath_lingering",
lava_drip_particle: "minecraft:lava_drip_particle",
water_drip_particle: "minecraft:water_drip_particle",
redstone_wire_dust_particle: "minecraft:redstone_wire_dust_particle",
sculk_sensor_redstone_particle: "minecraft:sculk_sensor_redstone_particle",
splash_spell_emitter: "minecraft:splash_spell_emitter",
electric_spark__particle: "minecraft:electric_spark__particle",
enchanting_table_particle: "minecraft:enchanting_table_particle",
endrod: "minecraft:endrod",
balloon_gas_particle: "minecraft:balloon_gas_particle",
evoker_spell: "minecraft:evoker_spell",
huge_explosion_emitter: "minecraft:huge_explosion_emitter",
falling_dust_red_sand_particle: "minecraft:falling_dust_red_sand_particle",
falling_dust_sand_particle: "minecraft:falling_dust_sand_particle",
falling_dust_gravel_particle: "minecraft:falling_dust_gravel_particle",
falling_dust_top_snow_particle: "minecraft:falling_dust_top_snow_particle",
falling_dust_dragon_egg_particle: "minecraft:falling_dust_dragon_egg_particle",
falling_dust_concrete_particle: "minecraft:falling_dust_concrete_particle",
falling_dust_scaffolding_particle: "minecraft:falling_dust_scaffolding_particle",
honey_drip_particle: "minecraft:honey_drip_particle",
nectar_drip_particle: "minecraft:nectar_drip_particle",
obsidian_tear_particle: "minecraft:obsidian_tear_particle",
spore_blossom_shower_particle: "minecraft:spore_blossom_shower_particle",
water_splash_particle: "minecraft:water_splash_particle",
sparkler_emitter: "minecraft:sparkler_emitter",
water_wake_particle: "minecraft:water_wake_particle",
basic_flame_particle: "minecraft:basic_flame_particle",
flash: "minecraft:flash",
glow_particle: "minecraft:glow_particle",
villager_happy: "minecraft:villager_happy",
heart_particle: "minecraft:heart_particle",
water_evaporation_actor_emitter: "minecraft:water_evaporation_actor_emitter",
lava_particle: "minecraft:lava_particle",
mobflame_emitter: "minecraft:mobflame_emitter",
mobflame_single: "minecraft:mobflame_single",
mycelium_dust_particle: "minecraft:mycelium_dust_particle",
note_particle: "minecraft:note_particle",
explode: "minecraft:explode",
mob_portal: "minecraft:mob_portal",
rainsplash: "minecraft:rainsplash",
basic_smoke_particle: "minecraft:basic_smoke_particle",
snowflake_particle: "minecraft:snowflake_particle",
soul_particle: "minecraft:soul_particle",
blue_flame_particle: "minecraft:blue_flame_particle",
spore_blossom_ambient_particle: "minecraft:spore_blossom_ambient_particle",
watersplash: "minecraft:watersplash",
terrain: "minecraft:terrain",
totem_particle: "minecraft:totem_particle",
tracking_emitter: "minecraft:tracking_emitter",
vibration_signal: "minecraft:vibration_signal",
wax_particle: "minecraft:wax_particle",
witchspell: "minecraft:witchspell",
point: "codecosmos:point",
};
class ParticleColumn {
pos: Vector3;
radius: number;
pointsPerLayer: number;
layerCount: number;
particle: string;
points: Vector3[] = [];
speed: number;
anglePerLayer: number[] = [];
tickCounter: number = 0;
constructor(
pos: Vector3,
radius: number,
pointsPerLayer: number,
layerCount: number,
speed: number,
particle: string
) {
this.pos = pos;
this.radius = radius;
this.particle = particle;
this.pointsPerLayer = pointsPerLayer;
this.layerCount = layerCount;
this.speed = speed;
this.generatePoints();
}
generatePoints() {
for (let layer = 0; layer < this.layerCount; layer++) {
let layerRadius = this.radius - layer;
for (let point = 0; point < this.pointsPerLayer; point++) {
let angle = (point / this.pointsPerLayer) * (Math.PI * 2);
this.anglePerLayer.push(angle);
let x = Math.cos(angle) * layerRadius;
let z = Math.sin(angle) * layerRadius;
let y = layer;
this.points.push(Vector3Add(this.pos, vector3(x, y, z)));
}
}
}
update() {
//update each point by increasing the angle by the speed
let layerCount = 0;
let pointCount = 0;
this.tickCounter++;
if (this.tickCounter % 2 == 0) {
this.points.forEach((point, index) => {
let layer = Math.floor(index / this.pointsPerLayer);
let angle = this.anglePerLayer[index];
angle += this.speed * (layer / 5);
this.anglePerLayer[index] = angle;
let layerRadius = this.radius - Math.floor(index / this.pointsPerLayer);
let calcAngle = angle;
let x = Math.cos(calcAngle) * this.radius;
let z = Math.sin(calcAngle) * this.radius;
let y = Math.floor(index / this.pointsPerLayer) + (this.tickCounter % 2);
this.points[index] = Vector3Add(this.pos, vector3(x, y, z));
});
}
}
draw() {
this.points.forEach((point) => {
spawnParticle(point);
});
}
}
function spawnParticle(
position: Vector3,
particle: string = PARTICLES.balloon_gas_particle,
map: MolangVariableMap = new MolangVariableMap()
) {
//check if the chunk is loaded
const chunk = world.getDimension("overworld").getBlock(position);
if (!chunk?.isValid()) {
return;
}
map.setVector3("variable.direction", vector3(0, 0, 0));
const dimension = world.getDimension("overworld");
if (dimension) {
dimension.spawnParticle(particle, position, map);
}
}
export { PARTICLES, bedrockParticles, ParticleColumn, spawnParticle };

View File

@@ -0,0 +1,71 @@
import { Vector3 } from "@minecraft/server";
function vector3(x: number, y: number, z: number): Vector3 {
return { x: x, y: y, z: z };
}
function Vector3ToString(vector: Vector3) {
return vector.x + "," + vector.y + "," + vector.z;
}
function Vector3ToCommandString(vector: Vector3) {
return `${vector.x} ${vector.y} ${vector.z}`;
}
function Vector3ToFancyString(vector: Vector3) {
return `{X: ${Math.floor(vector.x)}, Y: ${Math.floor(vector.y)}, Z: ${Math.floor(vector.z)}}`;
}
function Vector3Add(vector1: Vector3, vector2: Vector3): Vector3 {
return { x: vector1.x + vector2.x, y: vector1.y + vector2.y, z: vector1.z + vector2.z };
}
function Vector3Subtract(vector1: Vector3, vector2: Vector3): Vector3 {
return { x: vector1.x - vector2.x, y: vector1.y - vector2.y, z: vector1.z - vector2.z };
}
function Vector3Multiply(vector1: Vector3, vector2: Vector3): Vector3 {
return { x: vector1.x * vector2.x, y: vector1.y * vector2.y, z: vector1.z * vector2.z };
}
function Vector3Divide(vector1: Vector3, vector2: Vector3): Vector3 {
return { x: vector1.x / vector2.x, y: vector1.y / vector2.y, z: vector1.z / vector2.z };
}
function Vector3Floor(vector: Vector3): Vector3 {
return { x: Math.floor(vector.x), y: Math.floor(vector.y), z: Math.floor(vector.z) };
}
function Vector3Ceil(vector: Vector3): Vector3 {
return { x: Math.ceil(vector.x), y: Math.ceil(vector.y), z: Math.ceil(vector.z) };
}
function Vector3Round(vector: Vector3): Vector3 {
return { x: Math.round(vector.x), y: Math.round(vector.y), z: Math.round(vector.z) };
}
function Vector3Abs(vector: Vector3): Vector3 {
return { x: Math.abs(vector.x), y: Math.abs(vector.y), z: Math.abs(vector.z) };
}
function vector3Distance(vector1: Vector3, vector2: Vector3): number {
return Math.sqrt(
Math.pow(vector2.x - vector1.x, 2) + Math.pow(vector2.y - vector1.y, 2) + Math.pow(vector2.z - vector1.z, 2)
);
}
export {
Vector3ToString,
Vector3ToFancyString,
Vector3Add,
Vector3Subtract,
Vector3Multiply,
Vector3Divide,
Vector3Floor,
Vector3Ceil,
Vector3Round,
Vector3Abs,
vector3Distance,
Vector3ToCommandString,
vector3,
};

View File

@@ -0,0 +1,16 @@
import { system } from "@minecraft/server";
function delayedRun(callback: Function, delay: number) {
let timer = system.runTimeout(() => {
callback();
system.clearRun(timer);
}, delay);
}
function delay(t: number) {
return new Promise((r: any) => {
system.runTimeout(r, t);
});
}
export { delayedRun, delay };

View File

@@ -0,0 +1,34 @@
import { Vector3, world } from "@minecraft/server";
function clone(startPos: Vector3, endPos: Vector3, destination: Vector3) {
world
.getDimension("overworld")
.runCommand(
`clone ${startPos.x} ${startPos.y} ${startPos.z} ${endPos.x} ${endPos.y} ${endPos.z} ${destination.x} ${destination.y} ${destination.z} replace normal`
);
}
enum LeverDirection {
DownEastWest = "down_east_west",
DownNorthSouth = "down_north_south",
East = "east",
North = "north",
South = "south",
UpEastWest = "up_east_west",
UpNorthSouth = "up_north_south",
West = "west",
}
function setLever(pos: Vector3, direction: LeverDirection, isOpen: boolean) {
world
.getDimension("overworld")
.runCommandAsync(
`/setblock ${pos.x} ${pos.y} ${pos.z} lever["lever_direction":"${direction}","open_bit":${
isOpen ? "true" : "false"
}]`
);
///setblock 53 70 216 lever["lever_direction"="down_east_west","open_bit"=false]
}
export { clone, LeverDirection, setLever };