This commit is contained in:
Bram Verhulst
2024-07-04 14:42:38 +02:00
commit 67af73c6b5
60 changed files with 13407 additions and 0 deletions

9
.gitignore vendored Normal file
View File

@@ -0,0 +1,9 @@
./node_modules
./build
node_modules
build
export
.git

5
.idea/.gitignore generated vendored Normal file
View File

@@ -0,0 +1,5 @@
# Default ignored files
/shelf/
/workspace.xml
# Editor-based HTTP Client requests
/httpRequests/

12
.idea/Reeks2Missie6.iml generated Normal file
View File

@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="WEB_MODULE" version="4">
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$">
<excludeFolder url="file://$MODULE_DIR$/.tmp" />
<excludeFolder url="file://$MODULE_DIR$/temp" />
<excludeFolder url="file://$MODULE_DIR$/tmp" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>

57
.idea/codeStyles/Project.xml generated Normal file
View File

@@ -0,0 +1,57 @@
<component name="ProjectCodeStyleConfiguration">
<code_scheme name="Project" version="173">
<HTMLCodeStyleSettings>
<option name="HTML_SPACE_INSIDE_EMPTY_TAG" value="true" />
</HTMLCodeStyleSettings>
<JSCodeStyleSettings version="0">
<option name="FORCE_SEMICOLON_STYLE" value="true" />
<option name="SPACE_BEFORE_FUNCTION_LEFT_PARENTH" value="false" />
<option name="FORCE_QUOTE_STYlE" value="true" />
<option name="ENFORCE_TRAILING_COMMA" value="WhenMultiline" />
<option name="SPACES_WITHIN_OBJECT_LITERAL_BRACES" value="true" />
<option name="SPACES_WITHIN_IMPORTS" value="true" />
</JSCodeStyleSettings>
<TypeScriptCodeStyleSettings version="0">
<option name="FORCE_SEMICOLON_STYLE" value="true" />
<option name="SPACE_BEFORE_FUNCTION_LEFT_PARENTH" value="false" />
<option name="FORCE_QUOTE_STYlE" value="true" />
<option name="ENFORCE_TRAILING_COMMA" value="WhenMultiline" />
<option name="SPACES_WITHIN_OBJECT_LITERAL_BRACES" value="true" />
<option name="SPACES_WITHIN_IMPORTS" value="true" />
</TypeScriptCodeStyleSettings>
<VueCodeStyleSettings>
<option name="INTERPOLATION_NEW_LINE_AFTER_START_DELIMITER" value="false" />
<option name="INTERPOLATION_NEW_LINE_BEFORE_END_DELIMITER" value="false" />
</VueCodeStyleSettings>
<codeStyleSettings language="HTML">
<option name="SOFT_MARGINS" value="120" />
<indentOptions>
<option name="INDENT_SIZE" value="2" />
<option name="CONTINUATION_INDENT_SIZE" value="2" />
<option name="TAB_SIZE" value="2" />
</indentOptions>
</codeStyleSettings>
<codeStyleSettings language="JavaScript">
<option name="SOFT_MARGINS" value="120" />
<indentOptions>
<option name="INDENT_SIZE" value="2" />
<option name="CONTINUATION_INDENT_SIZE" value="2" />
<option name="TAB_SIZE" value="2" />
</indentOptions>
</codeStyleSettings>
<codeStyleSettings language="TypeScript">
<option name="SOFT_MARGINS" value="120" />
<indentOptions>
<option name="INDENT_SIZE" value="2" />
<option name="CONTINUATION_INDENT_SIZE" value="2" />
<option name="TAB_SIZE" value="2" />
</indentOptions>
</codeStyleSettings>
<codeStyleSettings language="Vue">
<option name="SOFT_MARGINS" value="120" />
<indentOptions>
<option name="CONTINUATION_INDENT_SIZE" value="2" />
</indentOptions>
</codeStyleSettings>
</code_scheme>
</component>

5
.idea/codeStyles/codeStyleConfig.xml generated Normal file
View File

@@ -0,0 +1,5 @@
<component name="ProjectCodeStyleConfiguration">
<state>
<option name="USE_PER_PROJECT_SETTINGS" value="true" />
</state>
</component>

4
.idea/encodings.xml generated Normal file
View File

@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="Encoding" addBOMForNewFiles="with BOM under Windows, with no BOM otherwise" />
</project>

8
.idea/modules.xml generated Normal file
View File

@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/Reeks2Missie6.iml" filepath="$PROJECT_DIR$/.idea/Reeks2Missie6.iml" />
</modules>
</component>
</project>

10
.prettierrc.json Normal file
View File

@@ -0,0 +1,10 @@
{
"trailingComma": "es5",
"tabWidth": 2,
"semi": true,
"singleQuote": false,
"tabs": false,
"backetSpacing": true,
"arrowParens": "always",
"printWidth": 120
}

7
.vscode/extensions.json vendored Normal file
View File

@@ -0,0 +1,7 @@
{
"recommendations": [
"esbenp.prettier-vscode",
"blockceptionltd.blockceptionvscodeminecraftbedrockdevelopmentextension",
"mojang-studios.minecraft-debugger"
]
}

16
.vscode/launch.json vendored Normal file
View File

@@ -0,0 +1,16 @@
{
"version": "0.3.0",
"configurations": [
{
"type": "minecraft-js",
"request": "attach",
"name": "Debug with Minecraft",
"mode": "listen",
"preLaunchTask": "build",
"targetModuleUuid": "868bf88f-78d9-4109-b1b2-308645e367bd",
"sourceMapRoot": "${workspaceFolder}/build/_Reeks2Missie6Debug",
"generatedSourceRoot": "${workspaceFolder}/build/behavior_packs/scripts",
"port": 19144
}
]
}

39
.vscode/settings.json vendored Normal file
View File

@@ -0,0 +1,39 @@
{
"[javascript]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[typescript]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[typescriptreact]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[json]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"git.ignoreLimitWarning": true,
"editor.formatOnSave": true,
"search.exclude": {
"**/.git": true,
"**/node_modules": true,
"**/build": true
},
"files.exclude": {
"**/.DS_Store": true,
"**/.git": true,
"**/build": false,
"**/node_modules": true
},
"cSpell.words": ["gametest", "gametests", "minecart", "shulker", "zoglin"],
"editor.tabSize": 2,
"workbench.colorCustomizations": {
"activityBar.background": "#243b1e",
"titleBar.activeBackground": "#1f3d21",
"titleBar.activeForeground": "#F5FCEB"
},
"emeraldwalk.runonsave": {
"commands": [{ "cmd": "gulp", "isAsync": true, "match": "\\.ts$" }]
},
"BC-MC.Education.Enable": true
}

12
.vscode/tasks.json vendored Normal file
View File

@@ -0,0 +1,12 @@
{
"version": "2.0.0",
"tasks": [
{
"label": "build",
"dependsOrder": "sequence",
"dependsOn": [
"gulp: default"
]
}
]
}

50
README.md Normal file
View File

@@ -0,0 +1,50 @@
# Codefever Typescript level framework
This is the readme for the typescript framework for minecraft education
# Terminology
## Puppeteer
Puppeteer is a class that handles all the UI code for displaying messages to the player. It also handles translations. see [Translations](#translations)
## MindKeeper
This is the system that handles all the world storage, supported types are `boolean`, `number`, `string`
> **Important** : all registering of the world variables must be done in the world.afterEvents.worldInitialize event (this event can't send anything to the world (like messages))
>
> Here is a example of a definition of the world variable foo
```typescript
world.afterEvents.worldInitialize.subscribe(({ propertyRegistry }) => {
mindKeeper.registerStore("foo", StoreType.string);
mindKeeper.registerToWorld(propertyRegistry);
});
```
## Level (could be abit convoluted) **WIP**
This defines a level in a world, it has 3 callback functions as parameters. These functions should
- define the setup logic
- define the update (loop) logic (Used for actionbar mainly)
- define the condition to pass the level
- define the code to be run when the level is completed
> There is a a AbstractCondition class included with a BlockCondition. This (convoluted) way you can define a BlockCondition in the function that checks if the level is complete.
## Translations
Translations work with a resource pack. It's the same as with regular resource packs. The way it works with Pupeteer is to set a message to the screen but add the "%" prefix to the key.
So `pupeteer.setActionbar("%foo.bar)` will look for the key `foo.bar` in the resource pack and display that message.
# Random knowlege
bunch of helper scripts for this [here](https://github.com/JaylyDev/ScriptAPI)
This could be handy [jaylydb](https://github.com/JaylyDev/ScriptAPI/tree/stable/scripts/jaylydb)
# Authors
- [Bram Verhulst](https://github.com/brammie15) :p

63
Setup.md Normal file
View File

@@ -0,0 +1,63 @@
# Setup
## Prerequisites
**Install Node.js tools, if you haven't already**
We're going to use the package manager [npm](https://www.npmjs.com/package/npm) to get more tools to make the process of building our project easier.
Visit [https://nodejs.org/](https://nodejs.org).
Download the version with "LTS" next to the number and install it. (LTS stands for Long Term Support, if you're curious.) In the Node.js Windows installer, accept the installation defaults. You do not need to install any additional tools for Native compilation.
**Install Visual Studio Code, if you haven't already**
Visit the [Visual Studio Code website](https://code.visualstudio.com) and install Visual Studio Code.
## Getting Started
1. Use npm to install our tools:
```powershell
npm i
```
1. When that's done, enter:
```powershell
npm i gulp-cli --global
```
It might also ask you to install the Minecraft Debugger and Blockception's Visual Studio Code plugin, which are plugins to Visual Studio Code that can help with Minecraft development. Go ahead and do that, if you haven't already.
# Running
To actually run the project, you must run `gulp` or `gulp watch` in the terminal.
# Gulpfile
At the top of the gulpfile there is a field for the name of the folder/project
# Manifest
In the behavior_packs folder change the name of the folder in that to match the name in the gulpfile.
In the same folder change the manifest.json to match your needs.
> `note`: this whole folder will be copied to the development_behavior_packs folder located at `%appdata%\Minecraft Education Edition\games\com.mojang\development_behavior_packs`
and if there is a resource_packs folder it will also be copied to the corresponding folder.
# World settings
Because education chose to be anoying (huge shock i know) you have to enable the js api manually through editing the world settings with nbt.
see a guide [here](https://wiki.bedrock.dev/nbt/experimental-education-edition.html)
# Debugging
To enable the conent log you have to enable the content log in the education settings.
Open `%appdata%\Minecraft Education Edition\games\com.mojang\minecraftpe\options.txt` and change `content_log_file` and `content_log_gui` to `1`.
Than ingame you can CTRL+H to open the content log.
> Note: it would be advised to change the keyboard settings because H defaults to show the tutorial witch can be anoying.

View File

View File

@@ -0,0 +1,33 @@
{
"format_version": 2,
"metadata": {
"authors": ["Me and my cat"]
},
"header": {
"name": "Reeks2Missie6BP",
"description": "De behavior pack voor Reeks 2 Missie 6",
"uuid": "868bf88f-78d9-4109-b1b2-308645e367bd",
"version": [1, 0, 0],
"min_engine_version": [1, 20, 0]
},
"modules": [
{
"description": "Script resources",
"language": "javascript",
"type": "script",
"uuid": "20c42d36-9e2c-47e1-b710-8a8497f184dc",
"version": [1, 0, 1],
"entry": "scripts/main.js"
}
],
"dependencies": [
{
"module_name": "@minecraft/server",
"version": "1.4.0-beta"
},
{
"uuid": "3c83eb6b-d574-411e-a430-f0e0ea816a3a",
"version": [1, 0, 0]
}
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

564
gulpfile.js Normal file
View File

@@ -0,0 +1,564 @@
// === CONFIGURABLE VARIABLES
const bpfoldername = "Reeks2Missie6";
const isStoreVersion = true;
const resource_pack_name = "Reeks2Missie6RP";
const resource_pack_description = "De resource pack voor Reeks 2 Missie 6";
const resource_pack_authors = ["Me and my cat"];
const behavior_pack_name = "Reeks2Missie6BP";
const behavior_pack_description = "De behavior pack voor Reeks 2 Missie 6";
const behavior_pack_authors = ["Me and my cat"];
// === Optional variables
const exportWorldFolderPath = "";
// === END CONFIGURABLE VARIABLES
const gulp = require("gulp");
const ts = require("gulp-typescript");
const del = require("del");
const os = require("os");
const spawn = require("child_process").spawn;
const sourcemaps = require("gulp-sourcemaps");
const zip = require("gulp-zip");
const fs = require("fs");
const rename = require("gulp-rename");
const crypto = require("crypto");
var readLineSync = import("readline-sync");
var NBT = import("nbtify");
const worldsFolderName = "minecraftWorlds";
const regularVersionMojangRoot = os.homedir() + "/appdata/Roaming/Minecraft Education Edition/games/com.mojang/";
const storeVersionMojangRoot =
os.homedir() +
"/AppData/Local/Packages/Microsoft.MinecraftEducationEdition_8wekyb3d8bbwe/LocalState/games/com.mojang/";
const mcdir = isStoreVersion ? storeVersionMojangRoot : regularVersionMojangRoot;
function clean_build(callbackFunction) {
del(["build/behavior_packs/", "build/resource_packs/"]).then(
(value) => {
callbackFunction(); // success
},
(reason) => {
callbackFunction(); // error
}
);
}
function clean_export(cb) {
del(["export/**/**"]).then(
(value) => {
cb(); // success
},
(reason) => {
cb(); // error
}
);
}
function copy_behavior_packs() {
return gulp.src(["behavior_packs/**/*"]).pipe(gulp.dest("build/behavior_packs"));
}
function copy_resource_packs() {
return gulp.src(["resource_packs/**/*"]).pipe(gulp.dest("build/resource_packs"));
}
const copy_content = gulp.parallel(copy_behavior_packs, copy_resource_packs);
function compile_scripts() {
return gulp
.src("scripts/**/*.ts")
.pipe(sourcemaps.init())
.pipe(
ts({
module: "es2020",
moduleResolution: "node",
lib: ["es2020", "dom"],
strict: true,
target: "es2020",
noImplicitAny: true,
})
)
.pipe(
sourcemaps.write("../../_" + bpfoldername + "Debug", {
destPath: bpfoldername + "/scripts/",
sourceRoot: "./../../../scripts/",
})
)
.pipe(gulp.dest("build/behavior_packs/scripts"));
}
const build = gulp.series(clean_build, copy_content, compile_scripts);
function clean_localmc(callbackFunction) {
if (!bpfoldername || !bpfoldername.length || bpfoldername.length < 2) {
console.log("No bpfoldername specified.");
callbackFunction();
return;
}
del([mcdir + "development_behavior_packs/" + bpfoldername, mcdir + "development_resource_packs/" + bpfoldername], {
force: true,
}).then(
(value) => {
callbackFunction(); // Success
},
(reason) => {
callbackFunction(); // Error
}
);
}
function deploy_localmc_behavior_packs() {
console.log("Deploying to '" + mcdir + "development_behavior_packs/" + bpfoldername + "'");
return gulp.src(["build/behavior_packs/**/*"]).pipe(gulp.dest(mcdir + "development_behavior_packs/" + bpfoldername));
}
function deploy_localmc_resource_packs() {
console.log("Deploying to '" + mcdir + "development_resource_packs/" + bpfoldername + "'");
return gulp.src(["build/resource_packs/**/*"]).pipe(gulp.dest(mcdir + "development_resource_packs/" + bpfoldername));
}
function getTargetWorldPath() {
return mcdir + worldsFolderName + "/" + activeWorldFolderName;
}
function getTargetConfigPath() {
return mcdir + "config";
}
function getTargetWorldBackupPath() {
return "backups/worlds/" + activeWorldFolderName;
}
function getDevConfigPath() {
return "config";
}
function getDevWorldPath() {
return "worlds/default";
}
function getDevWorldBackupPath() {
return "backups/worlds/devdefault";
}
function clean_localmc_world(callbackFunction) {
console.log("Removing '" + getTargetWorldPath() + "'");
del([getTargetWorldPath()], {
force: true,
}).then(
(value) => {
callbackFunction(); // Success
},
(reason) => {
callbackFunction(); // Error
}
);
}
function clean_localmc_config(callbackFunction) {
console.log("Removing '" + getTargetConfigPath() + "'");
del([getTargetConfigPath()], {
force: true,
}).then(
(value) => {
callbackFunction(); // Success
},
(reason) => {
callbackFunction(); // Error
}
);
}
function clean_dev_world(callbackFunction) {
console.log("Removing '" + getDevWorldPath() + "'");
del([getDevWorldPath()], {
force: true,
}).then(
(value) => {
callbackFunction(); // Success
},
(reason) => {
callbackFunction(); // Error
}
);
}
function clean_localmc_world_backup(callbackFunction) {
console.log("Removing backup'" + getTargetWorldBackupPath() + "'");
del([getTargetWorldBackupPath()], {
force: true,
}).then(
(value) => {
callbackFunction(); // Success
},
(reason) => {
callbackFunction(); // Error
}
);
}
function clean_dev_world_backup(callbackFunction) {
console.log("Removing backup'" + getDevWorldBackupPath() + "'");
del([getTargetWorldBackupPath()], {
force: true,
}).then(
(value) => {
callbackFunction(); // Success
},
(reason) => {
callbackFunction(); // Error
}
);
}
function backup_dev_world() {
console.log("Copying world '" + getDevWorldPath() + "' to '" + getDevWorldBackupPath() + "'");
return gulp
.src([getTargetWorldPath() + "/**/*"])
.pipe(gulp.dest(getDevWorldBackupPath() + "/worlds/" + activeWorldFolderName));
}
function deploy_localmc_config() {
console.log("Copying world 'config/' to '" + getTargetConfigPath() + "'");
return gulp.src([getDevConfigPath() + "/**/*"]).pipe(gulp.dest(getTargetConfigPath()));
}
function deploy_localmc_world() {
console.log("Copying world 'worlds/default/' to '" + getTargetWorldPath() + "'");
return gulp.src([getDevWorldPath() + "/**/*"]).pipe(gulp.dest(getTargetWorldPath()));
}
function ingest_localmc_world() {
console.log("Ingesting world '" + getTargetWorldPath() + "' to '" + getDevWorldPath() + "'");
return gulp.src([getTargetWorldPath() + "/**/*"]).pipe(gulp.dest(getDevWorldPath()));
}
function backup_localmc_world() {
console.log("Copying world '" + getTargetWorldPath() + "' to '" + getTargetWorldBackupPath() + "/'");
return gulp
.src([getTargetWorldPath() + "/**/*"])
.pipe(gulp.dest(getTargetWorldBackupPath() + "/" + activeWorldFolderName));
}
const deploy_localmc = gulp.series(
clean_localmc,
function (callbackFunction) {
callbackFunction();
},
gulp.parallel(deploy_localmc_behavior_packs, deploy_localmc_resource_packs)
);
function watch() {
return gulp.watch(
["scripts/**/*.ts", "behavior_packs/**/*", "resource_packs/**/*"],
gulp.series(build, deploy_localmc)
);
}
function serve() {
return gulp.watch(
["scripts/**/*.ts", "behavior_packs/**/*", "resource_packs/**/*"],
gulp.series(stopServer, build, deploy_localmc, startServer)
);
}
let activeServer = null;
function stopServer(callbackFunction) {
if (activeServer) {
activeServer.stdin.write("stop\n");
activeServer = null;
}
callbackFunction();
}
function startServer(callbackFunction) {
if (activeServer) {
activeServer.stdin.write("stop\n");
activeServer = null;
}
activeServer = spawn(dedicatedServerPath + "bedrock_server");
let logBuffer = "";
let serverLogger = function (buffer) {
let incomingBuffer = buffer.toString();
if (incomingBuffer.endsWith("\n")) {
(logBuffer + incomingBuffer).split(/\n/).forEach(function (message) {
if (message) {
if (message.indexOf("Server started.") >= 0) {
activeServer.stdin.write("script debugger listen 19144\n");
}
console.log("Server: " + message);
}
});
logBuffer = "";
} else {
logBuffer += incomingBuffer;
}
};
activeServer.stdout.on("data", serverLogger);
activeServer.stderr.on("data", serverLogger);
callbackFunction();
}
hasZipped = false;
function rename_zip_to_mcworld(cb) {
gulp
.src(["export/export.zip"])
.pipe(rename(bpfoldername + ".mcworld"))
.pipe(gulp.dest("export/"));
cb();
}
function zip_world(cb) {
let zip_path = "export/export.zip";
console.log("export/" + bpfoldername + "/**/**");
return gulp
.src(["export/" + bpfoldername + "/**/**"], { base: "export/" + bpfoldername + "/" })
.pipe(zip("export.zip", { compress: false, buffer: false }))
.pipe(gulp.dest("export/"));
}
function get_version() {
let currentTimeStamp = new Date().toISOString().replace(/T/, " ").replace(/\..+/, "");
return currentTimeStamp;
}
function add_version_to_world_name(cb) {
let worldNameFilePath = "export/" + bpfoldername + "/levelname.txt";
let worldName = fs.readFileSync(worldNameFilePath, "utf8");
currentTimeStamp = get_version();
worldName = worldName + " " + currentTimeStamp;
fs.writeFileSync(worldNameFilePath, worldName);
console.log("Written " + worldName + " to " + worldNameFilePath);
cb();
}
function copy_build_resource_pack_to_export_folder(cb) {
gulp
.src(["build/resource_packs/**/**"])
.pipe(gulp.dest("export/" + bpfoldername + "/resource_packs/" + bpfoldername));
console.log(
"Copied build/resource_packs/" +
bpfoldername +
"/**/** to export/" +
bpfoldername +
"/resource_packs/" +
bpfoldername
);
cb();
}
function copy_build_behavior_pack_to_export_folder(cb) {
gulp
.src(["build/behavior_packs/**/**"])
.pipe(gulp.dest("export/" + bpfoldername + "/behavior_packs/" + bpfoldername));
cb();
}
function copy_world_to_export_folder(cb) {
if (exportWorldFolderPath == "") {
var readLineSync = import("readline-sync").then((readLineSync) => {
let data = get_worlds_paths_and_names(cb);
let index = readLineSync.keyInSelect(
data.map((d) => d.name),
"Which world do you want to copy?"
);
if (index === -1) {
cb();
return;
}
const world = data[index];
//console.log("Copying world '" + world.path + "' to 'build/worlds/export/'");
return gulp.src([world.path + "/**/*"]).pipe(gulp.dest("export/" + bpfoldername));
});
} else {
return gulp.src([exportWorldFolderPath + "/**/*"]).pipe(gulp.dest("export/" + bpfoldername));
}
//copy the build to the correct folder
cb();
}
function get_worlds_paths_and_names(cb) {
let data = [];
const worldsPath = mcdir + worldsFolderName;
const worlds = fs.readdirSync(worldsPath);
worlds.forEach((world) => {
let worldName = "UNDEFINED";
try {
worldName = fs.readFileSync(worldsPath + "/" + world + "/levelname.txt", "utf8");
} catch (e) {
console.log("Error reading levelname.txt for " + world);
console.error(e);
}
data.push({
path: worldsPath + "/" + world,
name: worldName,
});
});
cb();
return data;
}
async function nbt_rename_world(cb) {
const NBT = await import("nbtify");
const { readFile, writeFile } = await import("fs/promises");
const buffer = await readFile("export/" + bpfoldername + "/level.dat");
const data = await NBT.read(buffer);
let oldLevelName = data.data.LevelName;
let newName = bpfoldername + " " + get_version();
data.data.LevelName = newName;
const result = await NBT.write(data);
await writeFile("export/" + bpfoldername + "/level.dat", result);
console.log("Renamed " + oldLevelName + " to " + newName);
cb();
}
function levelnametxt_rename(cb) {
const { readFile, writeFile } = import("fs/promises");
const levelNameFilePath = "export/" + bpfoldername + "/levelname.txt";
let levelName = fs.readFileSync(levelNameFilePath, "utf8");
let newName = bpfoldername + " " + get_version();
fs.writeFileSync(levelNameFilePath, newName);
console.log("Renamed " + levelName + " to " + newName);
cb();
}
// Setup functions
function setup_behaviour_pack(cb) {
const manifestPath = "behavior_packs/manifest.json";
const manifest = JSON.parse(fs.readFileSync(manifestPath, "utf8"));
let header_uuid = get_behaviour_pack_uuid(cb);
let module_uuid = generate_UUID(cb);
let resource_pack_dependencies_uuid = get_resource_pack_uuid(cb);
console.log("Header UUID: " + header_uuid);
console.log("Module UUID: " + module_uuid);
manifest.header.uuid = header_uuid;
manifest.modules[0].uuid = module_uuid;
manifest.dependencies[1].uuid = resource_pack_dependencies_uuid;
manifest.metadata.authors = behavior_pack_authors;
manifest.header.name = behavior_pack_name;
manifest.header.description = behavior_pack_description;
fs.writeFileSync(manifestPath, JSON.stringify(manifest, null, 1));
cb();
}
function setup_resource_pack(cb) {
const manifestPath = "resource_packs/manifest.json";
const manifest = JSON.parse(fs.readFileSync(manifestPath, "utf8"));
let header_uuid = get_resource_pack_uuid(cb);
let module_uuid = generate_UUID(cb);
let dependencies_uuid = get_behaviour_pack_uuid(cb);
manifest.header.uuid = header_uuid;
manifest.modules[0].uuid = module_uuid;
manifest.dependencies[0].uuid = dependencies_uuid;
manifest.metadata.authors = resource_pack_authors;
manifest.header.name = resource_pack_name;
manifest.header.description = resource_pack_description;
fs.writeFileSync(manifestPath, JSON.stringify(manifest, null, 1));
cb();
}
function generate_UUID(cb) {
let uuid = crypto.randomUUID();
return uuid;
}
var behavior_pack_uuid = null;
var resource_pack_uuid = null;
function get_uuid_for_type(cb, type) {
let to_return = null;
switch (type) {
case "behaviour_pack":
if (behavior_pack_uuid === null) {
behavior_pack_uuid = generate_UUID(cb);
}
to_return = behavior_pack_uuid;
break;
case "resource_pack":
if (resource_pack_uuid === null) {
resource_pack_uuid = generate_UUID(cb);
}
to_return = resource_pack_uuid;
break;
default:
return generate_UUID(cb);
}
return to_return;
}
let get_behaviour_pack_uuid = (cb) => get_uuid_for_type(cb, "behaviour_pack");
let get_resource_pack_uuid = (cb) => get_uuid_for_type(cb, "resource_pack");
exports.clean_build = clean_build;
exports.copy_behavior_packs = copy_behavior_packs;
exports.copy_resource_packs = copy_resource_packs;
exports.compile_scripts = compile_scripts;
exports.copy_content = copy_content;
exports.build = build;
exports.clean_localmc = clean_localmc;
exports.deploy_localmc = deploy_localmc;
exports.default = gulp.series(build, deploy_localmc);
exports.clean = gulp.series(clean_build, clean_localmc);
exports.watch = gulp.series(build, deploy_localmc, watch);
exports.serve = gulp.series(build, deploy_localmc, startServer, serve);
exports.updateworld = gulp.series(
clean_localmc_world_backup,
backup_localmc_world,
clean_localmc_world,
deploy_localmc_world
);
exports.ingestworld = gulp.series(clean_dev_world_backup, backup_dev_world, clean_dev_world, ingest_localmc_world);
exports.updateconfig = gulp.series(clean_localmc_config, deploy_localmc_config);
exports.compile_world = gulp.series(
clean_export,
build,
copy_world_to_export_folder,
gulp.parallel(copy_build_behavior_pack_to_export_folder, copy_build_resource_pack_to_export_folder),
gulp.parallel(add_version_to_world_name, nbt_rename_world)
);
exports.zip = gulp.series(zip_world, rename_zip_to_mcworld);
exports.export = gulp.series(
clean_export,
copy_world_to_export_folder,
build,
gulp.parallel(copy_build_behavior_pack_to_export_folder, copy_build_resource_pack_to_export_folder),
levelnametxt_rename,
nbt_rename_world,
zip_world,
rename_zip_to_mcworld
);
exports.test = gulp.parallel(setup_behaviour_pack, setup_resource_pack);
exports.export_clean = gulp.series(clean_export);

524
old_readme.md Normal file
View File

@@ -0,0 +1,524 @@
---
page_type: sample
author: mammerla
description: A basic Hello World example of developing Minecraft scripts using TypeScript and a build process.
ms.author: mikeam@microsoft.com
ms.date: 04/01/2022
languages:
- typescript
products:
- minecraft
---
# Minecraft TypeScript Starter Project
This sample demonstrates a simple build process and TypeScript compilation for Minecraft. This readme shows how you can use Betas APIs experiment to build out simple gameplay styles. You can use this project as a starter for your own scripting projects.
## Prerequisites
**Install Node.js tools, if you haven't already**
We're going to use the package manager [npm](https://www.npmjs.com/package/npm) to get more tools to make the process of building our project easier.
Visit [https://nodejs.org/](https://nodejs.org).
Download the version with "LTS" next to the number and install it. (LTS stands for Long Term Support, if you're curious.) In the Node.js Windows installer, accept the installation defaults. You do not need to install any additional tools for Native compilation.
**Install Visual Studio Code, if you haven't already**
Visit the [Visual Studio Code website](https://code.visualstudio.com) and install Visual Studio Code.
## Getting Started
1. To make your own environment look like the example, create a folder on your `C:\` drive and call it **projects**. Create a subfolder called **cotta**.
1. Put the extracted contents of the TypeScript Starter Project folder into **cotta**.
1. Open a Windows Terminal or PowerShell window and change the working directory to your **cotta** folder:
```powershell
cd c:\projects\cotta\
```
1. Use npm to install our tools:
```powershell
npm i
```
1. When that's done, enter:
```powershell
npm i gulp-cli --global
```
1. Use this shortcut command to open the project in Visual Studio Code:
```powershell
code .
```
It might also ask you to install the Minecraft Debugger and Blockception's Visual Studio Code plugin, which are plugins to Visual Studio Code that can help with Minecraft development. Go ahead and do that, if you haven't already.
### Chapter 1. Customize the behavior pack
In Visual Studio Code, expand the `behavior_packs` node in the treeview to the left, and rename the **cotta** folder to "cotta".
Use the Find/Replace command (Ctrl-Shift-F) to search for "cotta" and replace the instance in **gulpfile.js** and the instance in **launch.json** with "cotta."
Go back the Files tree view and open `behavior_packs\cotta\manifest.json`
Update the name and description properties to something like "Cotta Behavior Pack" and "My TypeScript Project".
Update the first and second UUID properties to make it unique to your project. See [this article](https://learn.microsoft.com/minecraft/creator/documents/behaviorpack) for tips on working with behavior packs and creating your own unique UUIDs.
> IMPORTANT:
> You may also need to update the version of Beta APIs in your `dependencies` section to match your version of Minecraft.
> Versions 1.19.40 feature `1.0.0-beta` APIs
> Versions 1.19.50, 1.19.60, and 1.19.70 feature `1.1.0-beta` APIs
> Versions 1.19.80 features `1.2.0-beta` APIs
> Versions 1.20.0 features `1.3.0-beta` APIs
> Versions 1.20.10 features `1.4.0-beta` APIs
> Versions 1.20.20 features `1.5.0-beta` APIs
> Future versions will likely require updated versions of Beta APIs.
### Chapter 2. Let's test the parts of our project
To get started, go into PowerShell and navigate to your **C:\projects\cotta** directory.
Run this command:
```powershell
Set-ExecutionPolicy -Scope Process -ExecutionPolicy Bypass
```
Run this one, too.
```powershell
gulp
```
This uses a build tool called GulpJS and automatically compiles your TypeScript project and pushes it over into Minecraft.
You may hear a little tone through your speakers when it has successfully completed deployment.
Launch Minecraft and create a new world:
1. Call it **Cotta Test**.
1. Select a Creative game mode.
1. Select a Flat world option, under the Advanced section of the Create New World screen.
1. Under Behavior Packs, under Available, you should see your Cotta Behavior Pack. Select it and Activate it.
1. Enable the Beta APIs experiment toggle, under the Experiments section of the Create New World screen.
1. Create the world and go into it.
Now you're in. Great!
By default, this starter pack comes with a simple script that will display a message:
`[Script Engine] Hello starter! Tick: <number>`
This means your behavior pack is working and your tools for compiling and pushing TypeScript are just fine. Awesome!
### Chapter 3. Scripting your gameplay
Let's go back to Visual Studio Code and change up some code.
Open up `scripts/main.ts` within Visual Studio Code.
#### Add some initialization code
Remove all the existing script code in **main.ts**. Replace it with this to start:
```typescript
import {
world,
system,
BlockPermutation,
EntityInventoryComponent,
ItemStack,
DisplaySlotId,
BlockType,
BlockTypes,
} from "@minecraft/server";
const START_TICK = 100;
// global variables
let curTick = 0;
const ARENA_X_SIZE = 30;
const ARENA_Z_SIZE = 30;
const ARENA_X_OFFSET = 0;
const ARENA_Y_OFFSET = -60;
const ARENA_Z_OFFSET = 0;
function initializeBreakTheTerracotta() {
const overworld = world.getDimension("overworld");
let scoreObjective = world.scoreboard.getObjective("score");
if (!scoreObjective) {
scoreObjective = world.scoreboard.addObjective("score", "Level");
}
// eliminate pesky nearby mobs
let entities = overworld.getEntities({
excludeTypes: ["player"],
});
for (let entity of entities) {
entity.kill();
}
// set up scoreboard
world.scoreboard.setObjectiveAtDisplaySlot(DisplaySlotId.Sidebar, {
objective: scoreObjective,
});
let players = world.getAllPlayers();
for (let player of players) {
player.runCommand("scoreboard players set @s score 0");
let inv = player.getComponent("inventory") as EntityInventoryComponent;
inv.container.addItem(new ItemStack("diamond_sword"));
inv.container.addItem(new ItemStack("dirt", 64));
player.teleport(
{
x: ARENA_X_OFFSET - 3,
y: ARENA_Y_OFFSET,
z: ARENA_Z_OFFSET - 3,
},
{
dimension: overworld,
rotation: { x: 0, y: 0 }
}
);
}
world.sendMessage("BREAK THE TERRACOTTA");
}
function gameTick() {
try {
curTick++;
if (curTick === START_TICK) {
initializeBreakTheTerracotta();
}
} catch (e) {
console.warn("Tick error: " + e);
}
system.run(gameTick);
}
system.run(gameTick);
```
This code does some work to initialize our gameplay for Minecraft by running several commands.
First, we queue up a run to our main tick function, gameTick. Note that at the end, we will requeue a game tick, which will run within the next tick frame. This will give us a callback that fires 20 times a second, and within this, we can put all of our game logic. We want the game to initialize some code; namely, the `initializeBreakTheTerracotta` function.
Note that we wait until `START_TICK` (100 ticks in) before the world is actually initialized. This gives Minecraft time to fully load up and get ready.
Within the initialize function, we run commands that:
- Clear out any existing mobs near the player in the world.
- Set up a scoreboard objective for overall Level of the player, meaning the number of terracotta breaks they have
- Give the current player a diamond sword and some dirty dirt
- Use chat to give the player an instructional message
Now, let's run the code. This time, we're going to run gulp in "watch mode" - meaning it will just sit in the background and watch for changes, and if they happen, they will automatically compile and deploy to the Minecraft folder. This way, we won't have to worry about separately compiling every time we make a change to code.
Go back to your PowerShell window, and enter:
```powershell
gulp watch
```
You should see gulp compile and deploy to the Minecraft folder, and make a noise when it does that. From here, we don't need to tend to PowerShell except to see if there are any compilation errors down the road.
When you are done coding for the day, either hit **ctrl-c** in the PowerShell Window to stop the watch mode or close the window.
Now, let's go back to Minecraft.
Save and Quit to exit out of the world. We'll want to reload the world from here - any time you make a script change, you need to exit out of the world and reload it to see changes. Or, you can run the `/reload` command to reload the JavaScript files that have been deployed.
Now load the world. You should see your initialization changes: a new scoreboard, new items in your inventory, and a script message.
Note that as you work through this tutorial, we are going to run the initialization code more than once, so your player is going to get multiples of these items during this development and test phase. If that bothers you, feel free to toss out these items before you close the world.
#### Build your arena with some helper code
We're going to start by adding some handy helper utility code functions. This will show you how you can organize your code into separate modules or classes.
Add a new file to your `scripts` folder called `Utilities.ts`. Correct capitalization matters, so make sure the `U` is capitalized. Add the following code:
```typescript
import { world, BlockType, BlockPermutation } from "@minecraft/server";
export default class Utilities {
static fillBlock(
blockType: BlockType,
xFrom: number,
yFrom: number,
zFrom: number,
xTo: number,
yTo: number,
zTo: number
) {
let overworld = world.getDimension("overworld");
let perm = BlockPermutation.resolve(blockType.id);
for (let i = xFrom; i <= xTo; i++) {
for (let j = yFrom; j <= yTo; j++) {
for (let k = zFrom; k <= zTo; k++) {
overworld.getBlock({ x: i, y: j, z: k })?.setPermutation(perm);
}
}
}
}
static fourWalls(
blockType: BlockType,
xFrom: number,
yFrom: number,
zFrom: number,
xTo: number,
yTo: number,
zTo: number
) {
let overworld = world.getDimension("overworld");
let perm = BlockPermutation.resolve(blockType.id);
for (let i = xFrom; i <= xTo; i++) {
for (let k = yFrom; k <= yTo; k++) {
overworld.getBlock({ x: i, y: k, z: zFrom })?.setPermutation(perm);
overworld.getBlock({ x: i, y: k, z: zTo })?.setPermutation(perm);
}
}
for (let j = zFrom + 1; j < zTo; j++) {
for (let k = yFrom; k <= yTo; k++) {
overworld.getBlock({ x: xFrom, y: k, z:j })?.setPermutation(perm);
overworld.getBlock({ x: xTo, y: k, z: j })?.setPermutation(perm);
}
}
}
}
```
The first utility function here (`Utilities.fillBlock`) is relatively straightforward:
Across three dimensions (within three loops), it will basically set a block in the overworld to a particular type. This function just makes a big chunk of blocks.
The second utility function here (`Utilities.fourWalls`) basically creates a walled enclave. The first inner loop creates two stripes of blocks left to right (across X). The second inner loop creates two stripes of blocks south to north (across Z) - thus completing four walls that join each other.
Go back to **main.ts**. Let's use these functions in our initialization function.
First, we'll need an import function. Add a new line above `const START_TICK = 100;` and make this the second line of the file:
```typescript
import Utilities from "./Utilities.js";
```
Next, within `initializeBreakTheTerracotta`, let's add our arena initialization beneath the `world.sendMessage("BREAK THE TERRACOTTA!");` line of code:
```typescript
let airBlockType = BlockTypes.get("minecraft:air");
let cobblestoneBlockType = BlockTypes.get("minecraft:cobblestone");
if (airBlockType) {
Utilities.fillBlock(
airBlockType,
ARENA_X_OFFSET - ARENA_X_SIZE / 2 + 1,
ARENA_Y_OFFSET,
ARENA_Z_OFFSET - ARENA_Z_SIZE / 2 + 1,
ARENA_X_OFFSET + ARENA_X_SIZE / 2 - 1,
ARENA_Y_OFFSET + 10,
ARENA_Z_OFFSET + ARENA_Z_SIZE / 2 - 1
);
}
if (cobblestoneBlockType) {
Utilities.fourWalls(
cobblestoneBlockType,
ARENA_X_OFFSET - ARENA_X_SIZE / 2,
ARENA_Y_OFFSET,
ARENA_Z_OFFSET - ARENA_Z_SIZE / 2,
ARENA_X_OFFSET + ARENA_X_SIZE / 2,
ARENA_Y_OFFSET + 10,
ARENA_Z_OFFSET + ARENA_Z_SIZE / 2
);
}
```
The first line just fills a cuboid with air - basically clearing out the arena of any previous items. The second line re-installs and adds four walls of cobblestone.
Exit out of your Minecraft world and restart it to load your changes. After a brief delay, you should find yourself in an arena.
Now, let's give ourselves some terracotta to break.
### Chapter 4. Add some gameplay basics - scoring and objectives
First, let's track some more game variables. Inside **main.ts**, add this directly beneath the `let curTick = 0` line of code:
```typescript
let score = 0;
let cottaX = 0;
let cottaZ = 0;
let spawnCountdown = 1;
```
Add the following to the `gameTick` function, beneath the `curTick++` line of code:
```typescript
if (curTick > START_TICK && curTick % 20 === 0) {
let overworld = world.getDimension("overworld");
// no terracotta exists, and we're waiting to spawn a new one.
if (spawnCountdown > 0) {
spawnCountdown--;
if (spawnCountdown <= 0) {
spawnNewTerracotta();
}
} else {
checkForTerracotta();
}
}
```
Now add the `spawnNewTerracotta()` and `checkForTerracotta()` functions after the last function and before the last `system.run(gameTick);` line of code:
```typescript
function spawnNewTerracotta() {
let overworld = world.getDimension("overworld");
// create new terracotta
cottaX = Math.floor(Math.random() * (ARENA_X_SIZE - 1)) - (ARENA_X_SIZE / 2 - 1);
cottaZ = Math.floor(Math.random() * (ARENA_Z_SIZE - 1)) - (ARENA_Z_SIZE / 2 - 1);
world.sendMessage("Creating new terracotta!");
let block = overworld
.getBlock({ x: cottaX + ARENA_X_OFFSET, y: 1 + ARENA_Y_OFFSET, z: cottaZ + ARENA_Z_OFFSET });
if (block) {
block.setPermutation(BlockPermutation.resolve("minecraft:yellow_glazed_terracotta"));
}
}
function checkForTerracotta() {
let overworld = world.getDimension("overworld");
let block = overworld.getBlock({ x: cottaX + ARENA_X_OFFSET, y: 1 + ARENA_Y_OFFSET, z: cottaZ + ARENA_Z_OFFSET });
if (block && !block.permutation.matches("minecraft:yellow_glazed_terracotta")) {
// we didn't find the terracotta! set a new spawn countdown
score++;
spawnCountdown = 2;
cottaX = -1;
let players = world.getAllPlayers();
for (let player of players) {
player.runCommand("scoreboard players set @s score " + score);
}
world.sendMessage("You broke the terracotta! Creating new terracotta in a few seconds.");
cottaZ = -1;
}
}
```
Congratulations! You've just created a very basic and very easy game where you can run around and break terracotta with your sword.
To play, you will need to run the command `/gamemode s` to put Minecraft into survival mode so that you can break the terracotta.
After the terracotta is broken, your score will increment, and a new block is spawned.
#### Add a challenge - let's add some mobs
OK, let's add this function after the `checkForTerracotta()` function:
```typescript
function spawnMobs() {
let overworld = world.getDimension("overworld");
// spawn mobs = create 1-2 mobs
let spawnMobCount = Math.floor(Math.random() * 2) + 1;
for (let j = 0; j < spawnMobCount; j++) {
let zombieX = Math.floor(Math.random() * (ARENA_X_SIZE - 2)) - ARENA_X_SIZE / 2;
let zombieZ = Math.floor(Math.random() * (ARENA_Z_SIZE - 2)) - ARENA_Z_SIZE / 2;
overworld.spawnEntity(
"minecraft:zombie",
{ x: zombieX + ARENA_X_OFFSET, y: 1 + ARENA_Y_OFFSET, z: zombieZ + ARENA_Z_OFFSET }
);
}
}
```
This function will spawn 1-2 zombies within the arena, at a random location. You can change the kinds of mobs to spawn, the number, and more within this function.
Let's call that function within our `gameTick` method:
```typescript
let spawnInterval = Math.ceil(200 / ((score + 1) / 3));
if (curTick > START_TICK && curTick % spawnInterval === 0) {
spawnMobs();
}
```
For gameplay, we want mobs to spawn more frequently as your score goes up. To do this, the frequency at which `spawnMobs` is called depends on the `spawnInterval` variable. `spawnInterval` is the span of time between spawning new mobs. Because we divide this interval by our current score, this means that as our score goes up, the interval of time between spawning mobs gets shorter. This makes the challenge harder over time.
As you play, zombies should spawn and start chasing you. They'll spawn slowly at first, but as you break blocks they'll start to accumulate and bother you while you try to break terracotta blocks.
### Add more challenges!
Let's add a new gameplay twist: randomly spawning obstructions in the form of leaves.
Add this function to **main.ts** to randomly place some fuzzy leaves:
```typescript
function addFuzzyLeaves() {
let overworld = world.getDimension("overworld");
for (let i = 0; i < 10; i++) {
const leafX = Math.floor(Math.random() * (ARENA_X_SIZE - 1)) - (ARENA_X_SIZE / 2 - 1);
const leafY = Math.floor(Math.random() * 10);
const leafZ = Math.floor(Math.random() * (ARENA_Z_SIZE - 1)) - (ARENA_Z_SIZE / 2 - 1);
overworld
.getBlock({ x: leafX + ARENA_X_OFFSET, y: leafY + ARENA_Y_OFFSET, z: leafZ + ARENA_Z_OFFSET})
?.setPermutation(BlockPermutation.resolve("minecraft:leaves"));
}
}
```
And call that function in your gameTick() function:
```typescript
if (curTick > START_TICK && curTick % 29 === 0) {
addFuzzyLeaves();
}
```
You may wonder why the interval here is 29. The main idea was to select a number to avoid the chance that on a particular tick we do everything at once (create new leaves, spawn mobs AND check terracotta state), so we try to have offset schedules for all of these different game activities.
Now exit out and reload your game. As you run around, you should see new leaves get spawned. This should add a little bit more challenge to your gameplay!
### Summary
With this starter, you've seen how to build a nice little arena game.
Like the randomly spawning leaves, you can see how you can add different gameplay elements into your arena. Maybe rather than leaves, you want to randomly generate some parkour platforms - or some treasures or weapons, or different types of mobs. Experiment and build your own custom competition arenas!
## Manifest
- [gulpfile.js](https://github.com/microsoft/minecraft-scripting-samples/blob/main/ts-starter/gulpfile.js): This file contains build instructions for Gulp, for building out TypeScript code.
- [scripts](https://github.com/microsoft/minecraft-scripting-samples/blob/main/ts-starter/scripts): This contains all of your TypeScript files, that will be compiled and built into your projects.
- [behavior_packs](https://github.com/microsoft/minecraft-scripting-samples/blob/main/ts-starter/behavior_packs): This contains resources and JSON files that define your behavior pack.

9450
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

29
package.json Normal file
View File

@@ -0,0 +1,29 @@
{
"name": "scripting-starter",
"version": "0.1.0",
"productName": "Minecraft TypeScript Starter Project",
"description": "Minecraft TypeScript Starter Project",
"private": true,
"devDependencies": {
"@minecraft/server-ui": "^1.1.0",
"del": "^6.0.0",
"gulp": "^4.0.2",
"gulp-cli": "^2.3.0",
"gulp-rename": "^2.0.0",
"gulp-sourcemaps": "^3.0.0",
"gulp-typescript": "^6.0.0-alpha.1",
"gulp-zip": "^5.1.0",
"nbtify": "^1.85.0",
"readline-sync": "^1.4.10",
"source-map": "^0.7.4",
"typescript": "^4.4.3"
},
"scripts": {
"enablemcloopback": "CheckNetIsolation.exe LoopbackExempt -a -p=S-1-15-2-1958404141-86561845-1752920682-3514627264-368642714-62675701-733520436",
"enablemcpreviewloopback": "CheckNetIsolation.exe LoopbackExempt -a -p=S-1-15-2-424268864-5579737-879501358-346833251-474568803-887069379-4040235476"
},
"dependencies": {
"@minecraft/server": "1.4.0-beta.1.20.10-stable",
"decode-uri-component": "^0.2.2"
}
}

View File

@@ -0,0 +1,26 @@
{
"format_version": 2,
"metadata": {
"authors": ["Me and my cat"]
},
"header": {
"name": "Reeks2Missie6RP",
"description": "De resource pack voor Reeks 2 Missie 6",
"min_engine_version": [1, 20, 0],
"uuid": "3c83eb6b-d574-411e-a430-f0e0ea816a3a",
"version": [1, 0, 0]
},
"modules": [
{
"type": "resources",
"uuid": "d563abd1-e684-42de-92f3-c9e08092609c",
"version": [1, 0, 0]
}
],
"dependencies": [
{
"uuid": "868bf88f-78d9-4109-b1b2-308645e367bd",
"version": [1, 0, 0]
}
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

View File

@@ -0,0 +1,37 @@
{
"format_version": "1.10.0",
"particle_effect": {
"description": {
"identifier": "codecosmos:point",
"basic_render_parameters": {
"material": "particles_alpha",
"texture": "textures/particle/point"
}
},
"components": {
"minecraft:emitter_rate_instant": {
"num_particles": 1
},
"minecraft:emitter_lifetime_once": {
"active_time": 1
},
"minecraft:emitter_shape_point": {},
"minecraft:particle_lifetime_expression": {
"max_lifetime": 0.1
},
"minecraft:particle_appearance_billboard": {
"size": [0.25, 0.25],
"facing_camera_mode": "rotate_xyz",
"uv": {
"texture_width": 16,
"texture_height": 16,
"uv": [0, 0],
"uv_size": [16, 16]
}
},
"minecraft:particle_appearance_tinting": {
"color": ["variable.color.r ", "variable.color.g", "variable.color.b", 1]
}
}
}
}

View File

View File

@@ -0,0 +1,3 @@
[
"en_US"
]

Binary file not shown.

After

Width:  |  Height:  |  Size: 107 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 107 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 168 B

View File

@@ -0,0 +1,72 @@
import {
Block,
ChatSendAfterEvent,
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";
export namespace TrailMaker {
export class Maker {
currentTrail: Trail;
log: Map<string, number> = new Map();
selectedIndex: number = 0;
OnChat(event: ChatSendAfterEvent) {}
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 pos = vector3(event.block.location.x, event.block.location.y, event.block.location.z);
pos = Vector3Add(pos, vector3(0.5, 0, 0.5));
pos = Vector3Add(pos, vector3(0, 1.1, 0));
this.currentTrail.points.push(new Point(pos, this.currentTrail.currentPoint));
//offset by half a block
this.currentTrail.currentPoint++;
world.sendMessage(`Added point ${this.currentTrail.currentPoint}`);
}
}
Update() {
this.currentTrail.points.forEach((point) => {
spawnParticle(point.position, "minecraft:balloon_gas_particle", new MolangVariableMap());
});
}
Export() {}
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 { 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,199 @@
import { Trigger } from "./trigger";
import {
Block,
ChatSendAfterEvent,
Color,
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");
}
}
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: Color): 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 = map.setColorRGB("variable.color", { red: 0, green: 255, blue: 0, alpha: 1.0 });
const particleData = new MolangVariableMap().setColorRGB("variable.color", {
red: 0,
green: 1,
blue: 0,
alpha: 1,
});
spawnParticle(vector3(x, y + 0.1, z), "codecosmos:point", particleData);
} else {
const particleData = new MolangVariableMap().setColorRGB("variable.color", color);
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()
? { red: 1, green: 0, blue: 0, alpha: 1.0 }
: { red: 1, green: 1, blue: 1, alpha: 1.0 };
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,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,48 @@
import { World } from "@minecraft/server";
class Level {
levelCompleteCallback: Function;
levelCheckCallback: Function;
levelSetupCallback: Function;
levelUpdateCallback: Function;
isCompleted: boolean = false;
isSetup: boolean = false;
constructor(
levelSetupCallback: Function,
levelUpdateCallback: Function,
levelCompleteCallback: Function,
levelCheckCallback: Function
) {
this.levelSetupCallback = levelSetupCallback;
this.levelCompleteCallback = levelCompleteCallback;
this.levelCheckCallback = levelCheckCallback;
this.levelUpdateCallback = levelUpdateCallback;
}
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.isCompleted = false;
}
}
//nextlevel
//mindkeeper
//pupeteer
export default Level;

View File

@@ -0,0 +1,44 @@
import { BlockType, MinecraftBlockTypes, Vector3, World } from "@minecraft/server";
export type blockCondition = {
block: BlockType;
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, DynamicPropertiesDefinition, PropertyRegistry, 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(propertyRegistry: PropertyRegistry) {
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.propertyManager.defineString(this.registerdStores[i].getName(), 25565);
this.debugLog.push("registerd string" + this.registerdStores[i].getName());
break;
case StoreType.number:
this.propertyManager.defineNumber(this.registerdStores[i].getName(), 0);
this.debugLog.push("registerd number" + this.registerdStores[i].getName());
break;
case StoreType.boolean:
this.propertyManager.defineBoolean(this.registerdStores[i].getName(), false);
this.debugLog.push("registerd boolean" + this.registerdStores[i].getName());
break;
}
}
propertyRegistry.registerWorldDynamicProperties(this.propertyManager);
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): 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 | 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.removeDynamicProperty(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,99 @@
import { RawText, TicksPerSecond, TitleDisplayOptions, Vector3, World, system } from "@minecraft/server";
import { delayedRun } from "./utils/waitUtil";
class Pupeteer {
world: World;
constructor(world: World) {
this.world = world;
}
private getActualString(message: string): string | RawText {
if (message.startsWith("%")) {
const key = message.substring(1);
return { rawtext: [{ translate: key }] };
}
return message;
}
setActionBarTimed(message: string, duration: number): void {
this.world.getPlayers().forEach((player) => {
player.onScreenDisplay.setActionBar(this.getActualString(message));
});
delayedRun(() => {
this.clearActionBar();
}, duration * TicksPerSecond);
}
setActionBar(message: string): void {
this.world.getPlayers().forEach((player) => {
player.onScreenDisplay.setActionBar(this.getActualString(message));
});
}
sendWorldMessage(message: string): void {
this.world.sendMessage(this.getActualString(message));
}
setTitleTimed(message: string, duration: number): void {
this.world.getPlayers().forEach((player) => {
let options: TitleDisplayOptions = {
fadeInDuration: 20,
fadeOutDuration: 20,
stayDuration: duration * TicksPerSecond,
};
player.onScreenDisplay.setTitle(this.getActualString(message), options);
});
}
setTitle(message: string): void {
this.world.getPlayers().forEach((player) => {
player.onScreenDisplay.setTitle(message);
});
}
updateSubtitle(message: string): void {
this.world.getPlayers().forEach((player) => {
player.onScreenDisplay.updateSubtitle(this.getActualString(message));
});
}
clearTitle(): void {
this.world.getPlayers().forEach((player) => {
player.onScreenDisplay.setTitle("");
});
}
clearSubtitle(): void {
this.world.getPlayers().forEach((player) => {
player.onScreenDisplay.updateSubtitle("");
});
}
clearActionBar(): void {
this.world.getPlayers().forEach((player) => {
player.onScreenDisplay.setActionBar("");
});
}
testForLocation(location: Vector3, radius: number): boolean {
let isPlayerInArea = false;
this.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;
}
setNpcText(npcTag: string, sceneName: string) {
this.world.getDimension("overworld").runCommand(`/dialogue change @e[tag=${npcTag}] ${sceneName} @a`);
}
}
export default Pupeteer;

View File

@@ -0,0 +1,80 @@
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 });
try {
world
.getDimension("overworld")
.spawnParticle("minecraft:balloon_gas_particle", spawnPosition, new MolangVariableMap());
} catch (e) {}
}
}
class Trail {
id: string;
points: TrailPoint[] = [];
currentPoint: number = 0;
nextParticleTimer: number = 0;
currentParticleCounter: number = 0;
calculatedLength: number = 0;
constructor(id: string, nextParticleTimer: number = 5) {
this.id = id;
this.nextParticleTimer = nextParticleTimer;
}
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;
this.points
.filter((point) => {
return point.index === 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,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,34 @@
import { MinecraftEntityTypes, Vector3, world } from "@minecraft/server";
import { Vector3ToString, vector3 } from "./vectorUtils";
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 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 };

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().setColorRGB("variable.color", {
red: 1,
green: 1,
blue: 1,
alpha: 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,20 @@
import { BlockType, MinecraftBlockTypes, Vector3, world } from "@minecraft/server";
type Wall = {
startPos: Vector3;
endPos: Vector3;
};
function clearWall(wall: Wall) {
world.getDimension("overworld").fillBlocks(wall.startPos, wall.endPos, MinecraftBlockTypes.air);
}
function fillWall(wall: Wall, block: BlockType) {
world.getDimension("overworld").fillBlocks(wall.startPos, wall.endPos, block);
}
function startLevel(commandBlockPos: Vector3) {
world.getDimension("overworld").fillBlocks(commandBlockPos, commandBlockPos, MinecraftBlockTypes.redstoneBlock);
}
export { Wall, clearWall, fillWall, startLevel };

View File

@@ -0,0 +1,231 @@
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;
}
const dimension = world.getDimension("overworld");
if (dimension) {
dimension.spawnParticle(particle, position, map);
}
}
export { PARTICLES, bedrockParticles, ParticleColumn, spawnParticle };

View File

@@ -0,0 +1,66 @@
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 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,
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 };

View File

@@ -0,0 +1,140 @@
import { BlockType, MinecraftBlockTypes, Vector3 } from "@minecraft/server";
import { LevelBlockCondition, LevelNoGoZone } from "../Commandeer/level/levelTypes";
import { vector3 } from "../Commandeer/utils/vectorUtils";
let level1Conditions: LevelBlockCondition = {
conditions: [
{
block: MinecraftBlockTypes.air,
position: vector3(55, 70, 216),
},
{
block: MinecraftBlockTypes.air,
position: vector3(56, 70, 216),
},
{
block: MinecraftBlockTypes.air,
position: vector3(57, 70, 216),
},
{
block: MinecraftBlockTypes.air,
position: vector3(58, 70, 216),
},
{
block: MinecraftBlockTypes.air,
position: vector3(59, 70, 216),
},
{
block: MinecraftBlockTypes.air,
position: vector3(60, 70, 216),
},
{
block: MinecraftBlockTypes.air,
position: vector3(61, 70, 216),
},
{
block: MinecraftBlockTypes.air,
position: vector3(62, 70, 216),
},
{
block: MinecraftBlockTypes.air,
position: vector3(63, 70, 216),
},
{
block: MinecraftBlockTypes.air,
position: vector3(64, 70, 216),
},
{
block: MinecraftBlockTypes.air,
position: vector3(65, 70, 216),
},
{
block: MinecraftBlockTypes.air,
position: vector3(66, 70, 216),
},
{
block: MinecraftBlockTypes.air,
position: vector3(67, 70, 216),
},
{
block: MinecraftBlockTypes.air,
position: vector3(68, 70, 216),
},
{
block: MinecraftBlockTypes.air,
position: vector3(69, 70, 216),
},
{
block: MinecraftBlockTypes.air,
position: vector3(70, 70, 216),
},
{
block: MinecraftBlockTypes.air,
position: vector3(71, 70, 216),
},
{
block: MinecraftBlockTypes.air,
position: vector3(72, 70, 216),
},
{
block: MinecraftBlockTypes.air,
position: vector3(73, 70, 216),
},
{
block: MinecraftBlockTypes.air,
position: vector3(74, 70, 216),
},
{
block: MinecraftBlockTypes.air,
position: vector3(74, 70, 216),
},
],
};
let level1NoGoZones: LevelNoGoZone = {
zones: [
{ position: vector3(55, 70, 215) },
{ position: vector3(56, 70, 215) },
{ position: vector3(57, 70, 215) },
{ position: vector3(58, 70, 215) },
{ position: vector3(59, 70, 215) },
{ position: vector3(60, 70, 215) },
{ position: vector3(61, 70, 215) },
{ position: vector3(62, 70, 215) },
{ position: vector3(63, 70, 215) },
{ position: vector3(64, 70, 215) },
{ position: vector3(65, 70, 215) },
{ position: vector3(66, 70, 215) },
{ position: vector3(67, 70, 215) },
{ position: vector3(68, 70, 215) },
{ position: vector3(69, 70, 215) },
{ position: vector3(70, 70, 215) },
{ position: vector3(71, 70, 215) },
{ position: vector3(72, 70, 215) },
{ position: vector3(73, 70, 215) },
{ position: vector3(74, 70, 215) },
{ position: vector3(55, 70, 217) },
{ position: vector3(56, 70, 217) },
{ position: vector3(57, 70, 217) },
{ position: vector3(58, 70, 217) },
{ position: vector3(59, 70, 217) },
{ position: vector3(60, 70, 217) },
{ position: vector3(61, 70, 217) },
{ position: vector3(62, 70, 217) },
{ position: vector3(63, 70, 217) },
{ position: vector3(64, 70, 217) },
{ position: vector3(65, 70, 217) },
{ position: vector3(66, 70, 217) },
{ position: vector3(67, 70, 217) },
{ position: vector3(68, 70, 217) },
{ position: vector3(69, 70, 217) },
{ position: vector3(70, 70, 217) },
{ position: vector3(71, 70, 217) },
{ position: vector3(72, 70, 217) },
{ position: vector3(73, 70, 217) },
{ position: vector3(74, 70, 217) },
],
};
export { level1Conditions, level1NoGoZones };

View File

@@ -0,0 +1,144 @@
import { BlockType, MinecraftBlockTypes, Vector3 } from "@minecraft/server";
import { LevelBlockCondition, LevelNoGoZone } from "../Commandeer/level/levelTypes";
import { vector3 } from "../Commandeer/utils/vectorUtils";
let level2Conditions: LevelBlockCondition = {
conditions: [
{
block: MinecraftBlockTypes.air,
position: vector3(45, 70, 220),
},
{
block: MinecraftBlockTypes.air,
position: vector3(44, 70, 220),
},
{
block: MinecraftBlockTypes.air,
position: vector3(43, 70, 220),
},
{
block: MinecraftBlockTypes.air,
position: vector3(42, 70, 220),
},
{
block: MinecraftBlockTypes.air,
position: vector3(41, 70, 220),
},
{
block: MinecraftBlockTypes.air,
position: vector3(40, 70, 220),
},
{
block: MinecraftBlockTypes.air,
position: vector3(39, 70, 220),
},
{
block: MinecraftBlockTypes.air,
position: vector3(38, 70, 220),
},
{
block: MinecraftBlockTypes.air,
position: vector3(37, 70, 220),
},
{
block: MinecraftBlockTypes.air,
position: vector3(36, 70, 220),
},
{
block: MinecraftBlockTypes.air,
position: vector3(35, 70, 220),
},
{
block: MinecraftBlockTypes.air,
position: vector3(34, 70, 220),
},
{
block: MinecraftBlockTypes.air,
position: vector3(33, 70, 220),
},
{
block: MinecraftBlockTypes.air,
position: vector3(33, 70, 219),
},
{
block: MinecraftBlockTypes.air,
position: vector3(33, 70, 218),
},
{
block: MinecraftBlockTypes.air,
position: vector3(33, 70, 217),
},
{
block: MinecraftBlockTypes.air,
position: vector3(33, 70, 216),
},
{
block: MinecraftBlockTypes.air,
position: vector3(33, 70, 215),
},
{
block: MinecraftBlockTypes.air,
position: vector3(33, 70, 214),
},
{
block: MinecraftBlockTypes.air,
position: vector3(33, 70, 213),
},
{
block: MinecraftBlockTypes.air,
position: vector3(33, 70, 212),
},
{
block: MinecraftBlockTypes.air,
position: vector3(34, 70, 212),
},
{
block: MinecraftBlockTypes.air,
position: vector3(35, 70, 212),
},
{
block: MinecraftBlockTypes.air,
position: vector3(36, 70, 212),
},
{
block: MinecraftBlockTypes.air,
position: vector3(37, 70, 212),
},
{
block: MinecraftBlockTypes.air,
position: vector3(38, 70, 212),
},
{
block: MinecraftBlockTypes.air,
position: vector3(39, 70, 212),
},
{
block: MinecraftBlockTypes.air,
position: vector3(40, 70, 212),
},
{
block: MinecraftBlockTypes.air,
position: vector3(41, 70, 212),
},
{
block: MinecraftBlockTypes.air,
position: vector3(42, 70, 212),
},
{
block: MinecraftBlockTypes.air,
position: vector3(43, 70, 212),
},
{
block: MinecraftBlockTypes.air,
position: vector3(44, 70, 212),
},
{
block: MinecraftBlockTypes.air,
position: vector3(45, 70, 212),
},
],
};
export { level2Conditions };

View File

@@ -0,0 +1,139 @@
import { BlockType, MinecraftBlockTypes, Vector3 } from "@minecraft/server";
import { LevelBlockCondition } from "../Commandeer/level/levelTypes";
import { vector3 } from "../Commandeer/utils/vectorUtils";
let level3Conditions: LevelBlockCondition = {
conditions: [
{
block: MinecraftBlockTypes.air,
position: vector3(57, 70, 235),
},
{
block: MinecraftBlockTypes.air,
position: vector3(58, 70, 235),
},
{
block: MinecraftBlockTypes.air,
position: vector3(59, 70, 235),
},
{
block: MinecraftBlockTypes.air,
position: vector3(60, 70, 235),
},
{
block: MinecraftBlockTypes.air,
position: vector3(60, 70, 234),
},
{
block: MinecraftBlockTypes.air,
position: vector3(60, 70, 233),
},
{
block: MinecraftBlockTypes.air,
position: vector3(60, 70, 232),
},
{
block: MinecraftBlockTypes.air,
position: vector3(60, 70, 231),
},
{
block: MinecraftBlockTypes.air,
position: vector3(61, 70, 231),
},
{
block: MinecraftBlockTypes.air,
position: vector3(62, 70, 231),
},
{
block: MinecraftBlockTypes.air,
position: vector3(63, 70, 231),
},
{
block: MinecraftBlockTypes.air,
position: vector3(64, 70, 231),
},
{
block: MinecraftBlockTypes.air,
position: vector3(64, 70, 232),
},
{
block: MinecraftBlockTypes.air,
position: vector3(64, 70, 233),
},
{
block: MinecraftBlockTypes.air,
position: vector3(64, 70, 234),
},
{
block: MinecraftBlockTypes.air,
position: vector3(64, 70, 235),
},
{
block: MinecraftBlockTypes.air,
position: vector3(64, 70, 236),
},
{
block: MinecraftBlockTypes.air,
position: vector3(64, 70, 237),
},
{
block: MinecraftBlockTypes.air,
position: vector3(64, 70, 238),
},
{
block: MinecraftBlockTypes.air,
position: vector3(64, 70, 239),
},
{
block: MinecraftBlockTypes.air,
position: vector3(65, 70, 239),
},
{
block: MinecraftBlockTypes.air,
position: vector3(66, 70, 239),
},
{
block: MinecraftBlockTypes.air,
position: vector3(67, 70, 239),
},
{
block: MinecraftBlockTypes.air,
position: vector3(68, 70, 239),
},
{
block: MinecraftBlockTypes.air,
position: vector3(68, 70, 238),
},
{
block: MinecraftBlockTypes.air,
position: vector3(68, 70, 237),
},
{
block: MinecraftBlockTypes.air,
position: vector3(68, 70, 236),
},
{
block: MinecraftBlockTypes.air,
position: vector3(68, 70, 235),
},
{
block: MinecraftBlockTypes.air,
position: vector3(69, 70, 235),
},
{
block: MinecraftBlockTypes.air,
position: vector3(70, 70, 235),
},
{
block: MinecraftBlockTypes.air,
position: vector3(71, 70, 235),
},
],
};
export { level3Conditions };

View File

@@ -0,0 +1,26 @@
import { MinecraftBlockTypes } from "@minecraft/server";
import { LevelBlockCondition, LevelLeverCondition } from "../Commandeer/level/levelTypes";
import { vector3 } from "../Commandeer/utils/vectorUtils";
let levelIntroConditions: LevelBlockCondition = {
conditions: [
{
block: MinecraftBlockTypes.emeraldBlock,
position: vector3(2471, 12, 108),
},
{
block: MinecraftBlockTypes.emeraldBlock,
position: vector3(2469, 12, 108),
},
{
block: MinecraftBlockTypes.lapisBlock,
position: vector3(2468, 12, 108),
},
{
block: MinecraftBlockTypes.lapisBlock,
position: vector3(2464, 12, 108),
},
],
};
export { levelIntroConditions };

223
scripts/main.ts Normal file
View File

@@ -0,0 +1,223 @@
import {
world,
system,
MinecraftBlockTypes,
Vector3,
MolangVariableMap,
ChatSendAfterEvent,
Player,
Dimension,
BlockType,
Block,
} from "@minecraft/server";
import { Mindkeeper, Store, StoreType } from "./Commandeer/mindKeeper";
import Pupeteer from "./Commandeer/pupeteer";
import Level from "./Commandeer/level/level";
import { leverOn } from "./Commandeer/level/levelTypes";
import { levelIntroConditions } from "./levelConditions/levelIntro";
import { Vector3ToFancyString, Vector3ToString, vector3 } from "./Commandeer/utils/vectorUtils";
import { delay } from "./Commandeer/utils/waitUtil";
import { PARTICLES, ParticleColumn, bedrockParticles, spawnParticle } from "./Commandeer/utils/particleUtils";
import { drawArrow } from "./Commandeer/utils/arrow";
import * as agentUtils from "./Commandeer/utils/agentUtils";
import { level3Conditions } from "./levelConditions/level3";
import { TrailMaker } from "./Commandeer/Makers/trailMaker";
import * as CCTrigger from "./Commandeer/Trigger/CCTrigger";
import { Command, Commands } from "./Commandeer/command/command";
const mindKeeper = new Mindkeeper(world);
const pupeteer = new Pupeteer(world);
const trailMaker: TrailMaker.Maker = new TrailMaker.Maker();
const triggerManager = new CCTrigger.Manager(mindKeeper);
const CURRENT_LEVEL = "currentLevel";
const AGENT_ID = "agentid";
const PREFIX = "!";
export { pupeteer, mindKeeper, CURRENT_LEVEL };
const DEVELOPER_MODE = true;
let tickCounter = 0;
system.runInterval(() => {
tickCounter++;
if (mindKeeper.initialised) {
trailMaker.Update();
updateIntro();
//run every 4 ticks
if (tickCounter % 2 == 0) {
triggerManager.Update();
}
const currentLevel = mindKeeper.get(CURRENT_LEVEL);
switch (currentLevel) {
}
}
});
const buttonPositions: Vector3[] = [
vector3(2471, 11, 106),
vector3(2469, 11, 106),
vector3(2468, 11, 106),
vector3(2464, 11, 106),
];
const blockPositions: Vector3[] = [
vector3(2471, 12, 108),
vector3(2469, 12, 108),
vector3(2468, 12, 108),
vector3(2464, 12, 108),
];
let currentBlockSequence: BlockType[] = [];
let currentBlockSeuqenceIndex: number[] = [];
triggerManager.RegisterFunctionTrigger("lightPath", (event) => {
lightUpPath();
});
//fill 2467 9 87 2468 9 105 redstone_block
async function lightUpPath() {
let overworld: Dimension = world.getDimension("overworld");
let pos1 = vector3(2467, 9, 87);
let pos2 = vector3(2468, 9, 105);
for (let z = pos2.z; z >= pos1.z; z--) {
overworld.fillBlocks(vector3(pos1.x, pos1.y, z), vector3(pos1.x + 1, pos1.y, z), MinecraftBlockTypes.redstoneBlock);
await delay(4);
}
// let pos2 = vector3(2468, 9, 105);
}
const blockCycle: BlockType[] = [
MinecraftBlockTypes.redstoneBlock,
MinecraftBlockTypes.goldBlock,
MinecraftBlockTypes.diamondBlock,
MinecraftBlockTypes.emeraldBlock,
MinecraftBlockTypes.lapisBlock,
];
let buttonPressed: boolean[] = buttonPositions.map(() => false);
function startIntro() {
//Determine the current squence
blockPositions.forEach((pos) => {
let block = world.getDimension("overworld").getBlock(pos);
let index = blockPositions.indexOf(pos);
let blockType = block!.type;
currentBlockSequence[index] = blockType;
});
}
function updateIntro() {
//Check each button if it's pressed
buttonPositions.forEach((pos) => {
let block = world.getDimension("overworld").getBlock(pos);
let index = buttonPositions.indexOf(pos);
let prevState = buttonPressed[index];
let currentState = block!.getRedstonePower()! > 0;
if (currentState && !prevState) {
buttonPressed[index] = true;
//NextBlock
let nextBlock = currentBlockSequence[index];
let nextIndex = blockCycle.indexOf(nextBlock);
nextIndex = (nextIndex + 1) % blockCycle.length;
currentBlockSequence[index] = blockCycle[nextIndex];
//Update the block
let blockPos = blockPositions[index];
world.getDimension("overworld").getBlock(blockPos)!.setType(blockCycle[nextIndex]);
}
if (!currentState && prevState) {
buttonPressed[index] = false;
}
});
}
world.afterEvents.worldInitialize.subscribe(({ propertyRegistry }) => {
mindKeeper.registerStore(CURRENT_LEVEL, StoreType.number);
mindKeeper.registerStore(AGENT_ID, StoreType.string);
// mindKeeper.set("ShowTriggers", true);
triggerManager.RegisterStores();
mindKeeper.registerToWorld(propertyRegistry);
triggerManager.Load();
});
world.beforeEvents.itemUseOn.subscribe((event) => {
trailMaker.OnItemUse(event);
triggerManager.OnItemUse(event);
});
function randomExplosions() {
let pos1 = vector3(2465, 10, 82);
let pos2 = vector3(2471, 18, 90);
for (let i = 0; i < 5; i++) {
let pos = vector3(
pos1.x + Math.random() * (pos2.x - pos1.x),
pos1.y + Math.random() * (pos2.y - pos1.y),
pos1.z + Math.random() * (pos2.z - pos1.z)
);
//create a particle
spawnParticle(pos, "minecraft:huge_explosion_emitter", new MolangVariableMap());
}
}
let originalDoorPos = vector3(2465, 10, 82);
let closedDoorPos = vector3(2465, 10, 82);
let openDoorPos = vector3(2465, 10, 82);
let doorSize = vector3(7, 7, 7);
///clone 2463 -10 81 2470 -3 87 2463 10 81
///clone 2463 -30 81 2470 -23 87 2463 10 81
function blowUpDoor() {
world.getDimension("overworld").runCommand("/clone 2463 -10 81 2470 -3 87 2463 10 81");
}
function restoreDoor() {
world.getDimension("overworld").runCommand("/clone 2463 -30 81 2470 -23 87 2463 10 81");
}
world.afterEvents.chatSend.subscribe((event: ChatSendAfterEvent) => {
const command = event.message.split(" ")[0];
trailMaker.OnChat(event);
mindKeeper.chatCommands(event);
triggerManager.OnChat(event);
if (command === "!reset") {
world.sendMessage("Resetting");
}
});
Commands.register(PREFIX, "info", (arg) => {
world.sendMessage("-----------------");
world.sendMessage("Current level: " + mindKeeper.get(CURRENT_LEVEL));
world.sendMessage("Agent ID: " + mindKeeper.get(AGENT_ID));
world.sendMessage("Engine Version: 1.0.2");
world.sendMessage("Engine is running");
world.sendMessage("Active players: " + world.getPlayers().length);
world.sendMessage("Current dimension: " + arg.player.dimension.id);
world.sendMessage("Current position: " + Vector3ToFancyString(arg.player.location));
world.sendMessage("-----------------");
});
Commands.register(PREFIX, "kboom", (arg) => {
(async () => {
for (let i = 0; i < 10; i++) {
randomExplosions();
await delay(1);
}
blowUpDoor();
})();
});
Commands.register(PREFIX, "restore", restoreDoor);
system.afterEvents.scriptEventReceive.subscribe((event) => {
if (event.id == "cc:getId") {
let id = event.message;
world.sendMessage("Script got the id " + id);
mindKeeper.set(AGENT_ID, id);
}
});

View File

@@ -0,0 +1,66 @@
import { Vector3 } from "@minecraft/server";
import { TrailType } from "../Commandeer/trail/trailTypes";
import { vector3 } from "../Commandeer/utils/vectorUtils";
let startTrail: TrailType = {
name: "startTrail",
points: [
{ index: 0, position: vector3(50, 70, 266) },
{ index: 1, position: vector3(50, 70, 265) },
{ index: 2, position: vector3(50, 70, 264) },
{ index: 3, position: vector3(50, 70, 263) },
{ index: 4, position: vector3(50, 70, 262) },
{ index: 5, position: vector3(50, 70, 261) },
{ index: 6, position: vector3(50, 70, 260) },
{ index: 7, position: vector3(50, 70, 259) },
{ index: 8, position: vector3(50, 70, 258) },
{ index: 9, position: vector3(50, 70, 257) },
{ index: 10, position: vector3(50, 70, 256) },
{ index: 11, position: vector3(50, 70, 255) },
{ index: 12, position: vector3(50, 70, 254) },
{ index: 13, position: vector3(50, 70, 253) },
{ index: 14, position: vector3(50, 70, 252) },
{ index: 15, position: vector3(50, 70, 251) },
{ index: 16, position: vector3(50, 70, 250) },
{ index: 17, position: vector3(50, 70, 249) },
{ index: 18, position: vector3(50, 70, 248) },
{ index: 19, position: vector3(50, 70, 247) },
{ index: 20, position: vector3(50, 70, 246) },
{ index: 21, position: vector3(50, 70, 245) },
{ index: 22, position: vector3(50, 70, 244) },
{ index: 23, position: vector3(50, 70, 243) },
{ index: 24, position: vector3(50, 70, 242) },
{ index: 25, position: vector3(50, 70, 241) },
{ index: 26, position: vector3(50, 70, 240) },
{ index: 27, position: vector3(50, 70, 239) },
{ index: 1, position: vector3(50, 70, 238) },
{ index: 2, position: vector3(50, 70, 237) },
{ index: 3, position: vector3(50, 70, 236) },
{ index: 4, position: vector3(50, 70, 235) },
{ index: 5, position: vector3(50, 70, 234) },
{ index: 6, position: vector3(50, 70, 233) },
{ index: 7, position: vector3(50, 70, 232) },
{ index: 8, position: vector3(50, 70, 231) },
{ index: 9, position: vector3(50, 70, 230) },
{ index: 10, position: vector3(50, 70, 229) },
{ index: 11, position: vector3(50, 70, 228) },
{ index: 12, position: vector3(50, 70, 227) },
{ index: 13, position: vector3(50, 70, 226) },
{ index: 14, position: vector3(50, 70, 225) },
{ index: 15, position: vector3(50, 70, 224) },
{ index: 16, position: vector3(50, 70, 223) },
{ index: 17, position: vector3(50, 70, 222) },
{ index: 18, position: vector3(50, 70, 221) },
{ index: 19, position: vector3(50, 70, 220) },
{ index: 20, position: vector3(50, 70, 219) },
{ index: 21, position: vector3(50, 70, 218) },
{ index: 22, position: vector3(50, 70, 217) },
{ index: 23, position: vector3(50, 70, 216) },
{ index: 24, position: vector3(51, 70, 216) },
{ index: 25, position: vector3(52, 70, 216) },
{ index: 26, position: vector3(53, 70, 216) },
{ index: 27, position: vector3(54, 70, 216) },
],
};
export { startTrail };

45
scripts/triggers.ts Normal file
View File

@@ -0,0 +1,45 @@
import { Dimension, MinecraftBlockTypes, world } from "@minecraft/server";
import { CCTrigger } from "./Commandeer/Trigger/CCTrigger";
import { mindKeeper } from "./main";
import { vector3 } from "./Commandeer/utils/vectorUtils";
import { delay } from "./Commandeer/utils/waitUtil";
const triggerManager = new CCTrigger.Manager(mindKeeper);
triggerManager.RegisterFunctionTrigger("test", (event) => {
world.sendMessage("Wow, this is a trigger :O");
world.sendMessage("This was caused by " + event.player.name);
});
triggerManager.RegisterFunctionTrigger("lightPath", (event) => {
lightUpPath();
});
triggerManager.RegisterFunctionTrigger("resetPath", (event) => {
resetLightPath();
});
//fill 2467 9 87 2468 9 105 redstone_block
async function lightUpPath() {
let overworld: Dimension = world.getDimension("overworld");
let pos1 = vector3(2467, 9, 87);
let pos2 = vector3(2468, 9, 105);
for (let z = pos2.z; z >= pos1.z; z--) {
overworld.fillBlocks(vector3(pos1.x, pos1.y, z), vector3(pos1.x + 1, pos1.y, z), MinecraftBlockTypes.redstoneBlock);
await delay(4);
}
// let pos2 = vector3(2468, 9, 105);
}
async function resetLightPath() {
world.getDimension("overworld").runCommand("/fill 2467 9 87 2468 9 105 air");
}
triggerManager.RegisterFunctionTrigger("test2", (event) => {
world.sendMessage("Wow, this is another trigger :O");
});
triggerManager.RegisterFunctionTrigger("die", (event) => {
world.sendMessage("You died");
event.player.applyDamage(1000);
});

32
tsconfig.json Normal file
View File

@@ -0,0 +1,32 @@
{
"compilerOptions":{
"target":"es6",
"moduleResolution":"node",
"module":"es2020",
"declaration":false,
"noLib":false,
"emitDecoratorMetadata":true,
"experimentalDecorators":true,
"sourceMap":true,
"pretty":true,
"forceConsistentCasingInFileNames": true,
"strict": true,
"allowUnreachableCode":true,
"allowUnusedLabels":true,
"noImplicitAny":true,
"noImplicitReturns":false,
"noImplicitUseStrict":false,
"outDir":"build/",
"rootDir": ".",
"baseUrl":"behavior_packs/",
"listFiles":false,
"noEmitHelpers":true
},
"include":[
"scripts/**/*"
],
"exclude":[
"node_modules"
],
"compileOnSave":false
}