Removed a bunch of alkatest stuff, added hex grid
This commit is contained in:
parent
36732d26d8
commit
5059098aca
14 changed files with 203 additions and 3177 deletions
|
@ -1 +0,0 @@
|
||||||
Subproject commit 8fd9cd1ba44d624fd347aab1ce762512e84d259e
|
|
|
@ -149,6 +149,7 @@ function openDiscord() {
|
||||||
height: 46px;
|
height: 46px;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
border-bottom: 4px solid var(--outline);
|
border-bottom: 4px solid var(--outline);
|
||||||
|
z-index: 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
.nav > * {
|
.nav > * {
|
||||||
|
|
136
src/data/HexGrid.vue
Normal file
136
src/data/HexGrid.vue
Normal file
|
@ -0,0 +1,136 @@
|
||||||
|
<template>
|
||||||
|
<div class="grid">
|
||||||
|
<div v-for="(row, rowIndex) in gridData" :key="rowIndex" class="row">
|
||||||
|
<transition-group name="drop" tag="div">
|
||||||
|
<div
|
||||||
|
v-for="(color, colIndex) in row"
|
||||||
|
:key="`${rowIndex}-${colIndex}-${color}`"
|
||||||
|
class="hexagon-wrapper"
|
||||||
|
:style="{ '--color': color }"
|
||||||
|
@click="updateColor(rowIndex, colIndex)"
|
||||||
|
@mouseover="setHoveredCell(rowIndex, colIndex)"
|
||||||
|
@mouseout="resetHoveredCell"
|
||||||
|
>
|
||||||
|
<div class="hexagon" :style="{ backgroundColor: color }"></div>
|
||||||
|
<div
|
||||||
|
v-if="
|
||||||
|
hoveredCell?.row === rowIndex &&
|
||||||
|
hoveredCell?.col === colIndex &&
|
||||||
|
color !== selectedColor
|
||||||
|
"
|
||||||
|
class="hexagon-overlay"
|
||||||
|
:style="{ '--color': selectedColor }"
|
||||||
|
>
|
||||||
|
<div class="hexagon" :style="{ backgroundColor: selectedColor }"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</transition-group>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { ref, toRefs } from "vue";
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
gridData: string[][];
|
||||||
|
selectedColor: string;
|
||||||
|
}>();
|
||||||
|
|
||||||
|
const emit = defineEmits<{
|
||||||
|
(event: "updateColor", row: number, col: number, color: string): void;
|
||||||
|
}>();
|
||||||
|
|
||||||
|
const { selectedColor } = toRefs(props);
|
||||||
|
const hoveredCell = ref<{ row: number; col: number } | null>(null);
|
||||||
|
|
||||||
|
function updateColor(row: number, col: number) {
|
||||||
|
emit("updateColor", row, col, selectedColor.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
function setHoveredCell(row: number, col: number) {
|
||||||
|
hoveredCell.value = { row, col };
|
||||||
|
}
|
||||||
|
|
||||||
|
function resetHoveredCell() {
|
||||||
|
hoveredCell.value = null;
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.grid {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.row {
|
||||||
|
margin-top: -1vmin;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hexagon-wrapper {
|
||||||
|
position: relative;
|
||||||
|
width: 5vmin;
|
||||||
|
height: 5vmin;
|
||||||
|
margin: 0.2vmin;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hexagon-wrapper::before,
|
||||||
|
.hexagon-wrapper::after,
|
||||||
|
.hexagon-overlay::before,
|
||||||
|
.hexagon-overlay::after {
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
bottom: 0.1vmin;
|
||||||
|
width: 50%;
|
||||||
|
height: 0.6vmin;
|
||||||
|
backdrop-filter: blur(3px);
|
||||||
|
background-color: var(--color);
|
||||||
|
transform-origin: bottom;
|
||||||
|
pointer-events: none;
|
||||||
|
z-index: -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hexagon-wrapper::before,
|
||||||
|
.hexagon-overlay::before {
|
||||||
|
left: 0;
|
||||||
|
transform: skewY(26.5deg);
|
||||||
|
filter: brightness(85%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.hexagon-wrapper::after,
|
||||||
|
.hexagon-overlay::after {
|
||||||
|
right: 0;
|
||||||
|
transform: skewY(-26.5deg);
|
||||||
|
filter: brightness(65%);
|
||||||
|
}
|
||||||
|
|
||||||
|
.hexagon {
|
||||||
|
position: relative;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
backdrop-filter: blur(3px);
|
||||||
|
clip-path: polygon(50% 0%, 100% 25%, 100% 75%, 50% 100%, 0% 75%, 0% 25%);
|
||||||
|
transition-duration: 0s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hexagon-overlay {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
opacity: 0.5;
|
||||||
|
pointer-events: none;
|
||||||
|
animation: bounce 1s infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes bounce {
|
||||||
|
0%, 100% {
|
||||||
|
transform: translateY(-15px);
|
||||||
|
}
|
||||||
|
50% {
|
||||||
|
transform: translateY(-7.5px);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -19,7 +19,6 @@
|
||||||
<h3>{{ room.name }}</h3>
|
<h3>{{ room.name }}</h3>
|
||||||
</button>
|
</button>
|
||||||
<div class="room-host">Hosted by {{ room.host }}</div>
|
<div class="room-host">Hosted by {{ room.host }}</div>
|
||||||
<div class="room-host">{{ room.numContentPacks }} active content packs</div>
|
|
||||||
</div>
|
</div>
|
||||||
<div v-else class="details" style="display: flex">
|
<div v-else class="details" style="display: flex">
|
||||||
<span>Password:</span>
|
<span>Password:</span>
|
||||||
|
@ -39,7 +38,13 @@ import { ref, toRefs, watch } from "vue";
|
||||||
import Text from "components/fields/Text.vue";
|
import Text from "components/fields/Text.vue";
|
||||||
import { Direction } from "util/common";
|
import { Direction } from "util/common";
|
||||||
import Tooltip from "features/tooltips/Tooltip.vue";
|
import Tooltip from "features/tooltips/Tooltip.vue";
|
||||||
import { ClientRoomData } from "alkatest-common/types";
|
|
||||||
|
// For some reason it won't find the interface from chromatic-common
|
||||||
|
interface ClientRoomData {
|
||||||
|
name: string;
|
||||||
|
host: string;
|
||||||
|
hasPassword: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
const _props = defineProps<{
|
const _props = defineProps<{
|
||||||
isPrivate: boolean;
|
isPrivate: boolean;
|
||||||
|
|
|
@ -60,17 +60,16 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="tsx">
|
<script setup lang="tsx">
|
||||||
import type { ClientRoomData } from "alkatest-common/types";
|
import Modal from "components/Modal.vue";
|
||||||
import Text from "components/fields/Text.vue";
|
import Text from "components/fields/Text.vue";
|
||||||
import Toggle from "components/fields/Toggle.vue";
|
import Toggle from "components/fields/Toggle.vue";
|
||||||
import Modal from "components/Modal.vue";
|
|
||||||
import {
|
import {
|
||||||
connected,
|
connected,
|
||||||
|
room as currentRoom,
|
||||||
emit as emitToServer,
|
emit as emitToServer,
|
||||||
getGameState,
|
getGameState,
|
||||||
isHosting,
|
isHosting,
|
||||||
nicknames,
|
nicknames
|
||||||
room as currentRoom
|
|
||||||
} from "data/socket";
|
} from "data/socket";
|
||||||
import { jsx } from "features/feature";
|
import { jsx } from "features/feature";
|
||||||
import { createTabFamily } from "features/tabs/tabFamily";
|
import { createTabFamily } from "features/tabs/tabFamily";
|
||||||
|
@ -80,9 +79,15 @@ import settings from "game/settings";
|
||||||
import { render } from "util/vue";
|
import { render } from "util/vue";
|
||||||
import type { ComponentPublicInstance } from "vue";
|
import type { ComponentPublicInstance } from "vue";
|
||||||
import { ref, watch } from "vue";
|
import { ref, watch } from "vue";
|
||||||
import { main } from "./projEntry";
|
|
||||||
import Room from "./Room.vue";
|
import Room from "./Room.vue";
|
||||||
|
|
||||||
|
// For some reason it won't find the interface from chromatic-common
|
||||||
|
interface ClientRoomData {
|
||||||
|
name: string;
|
||||||
|
host: string;
|
||||||
|
hasPassword: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
export type LoadablePlayerData = Omit<Partial<PlayerData>, "id"> & { id: string; error?: unknown };
|
export type LoadablePlayerData = Omit<Partial<PlayerData>, "id"> & { id: string; error?: unknown };
|
||||||
|
|
||||||
const isOpen = ref(false);
|
const isOpen = ref(false);
|
||||||
|
@ -241,7 +246,6 @@ function host(room: string, password?: string, privateRoom?: boolean) {
|
||||||
name: room,
|
name: room,
|
||||||
password,
|
password,
|
||||||
nickname: settings.nickname,
|
nickname: settings.nickname,
|
||||||
contentPacks: main.contentPacks.value,
|
|
||||||
privateRoom: privateRoom === true,
|
privateRoom: privateRoom === true,
|
||||||
state: getGameState()
|
state: getGameState()
|
||||||
});
|
});
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,818 +0,0 @@
|
||||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
||||||
import { computed, ComputedRef } from "@vue/reactivity";
|
|
||||||
import { isObject } from "@vue/shared";
|
|
||||||
import type { ActionBlock, ArrayBlock, CreateDictionaryBlock } from "alkatest-common/types";
|
|
||||||
import { getUniqueNodeID } from "features/boards/board";
|
|
||||||
import {
|
|
||||||
validateActionBlock,
|
|
||||||
validateArrayBlock,
|
|
||||||
validateBooleanBlock,
|
|
||||||
validateDictionaryBlock,
|
|
||||||
validateEntryBlock,
|
|
||||||
validateItemStackBlock,
|
|
||||||
validateInventoryBlock,
|
|
||||||
validateNodeActionBlock,
|
|
||||||
validateNumberBlock,
|
|
||||||
validatePositionBlock,
|
|
||||||
validateSizeBlock,
|
|
||||||
validateStringBlock,
|
|
||||||
validateObjectBlock,
|
|
||||||
validateReferenceBlock
|
|
||||||
} from "./contentPackValidation";
|
|
||||||
import { main } from "./projEntry";
|
|
||||||
|
|
||||||
export type Context = Record<string, Record<string, any>>;
|
|
||||||
|
|
||||||
export const BreakAction = Symbol("Break");
|
|
||||||
export const ReturnAction = Symbol("Return");
|
|
||||||
|
|
||||||
export function resolveEvent(event: string, data: any) {
|
|
||||||
let context = main.customObjects.value;
|
|
||||||
let contextPrefix = "@";
|
|
||||||
let i = 1;
|
|
||||||
while (`${contextPrefix}data` in context) {
|
|
||||||
contextPrefix = `${++i}@`;
|
|
||||||
}
|
|
||||||
context = { ...context, [`${contextPrefix}iteration`]: data };
|
|
||||||
main.events.value[event].forEach((actions, i) => {
|
|
||||||
try {
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
||||||
resolveActionArrayBlock(actions, context, []);
|
|
||||||
} catch (exception) {
|
|
||||||
console.error(
|
|
||||||
formatExceptionMessage(
|
|
||||||
`Failed to run event '${event}' listener #${i}`,
|
|
||||||
exception as { message: string; stack: string[] }
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
export function formatExceptionMessage(
|
|
||||||
resolution: string,
|
|
||||||
{ stack, message }: { stack: string[]; message: string }
|
|
||||||
) {
|
|
||||||
return `${resolution}\n${stack.join(".")}: ${message}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function resolveToComputed<T extends object, S extends keyof T, R = T[S]>(
|
|
||||||
block: T,
|
|
||||||
property: S,
|
|
||||||
resolveFunction: (block: any, context: Context, stack: string[]) => R
|
|
||||||
): ComputedRef<R | null> {
|
|
||||||
return computed(() => {
|
|
||||||
try {
|
|
||||||
return resolveFunction(block[property], main.customObjects.value, [property as string]);
|
|
||||||
} catch (exception) {
|
|
||||||
console.error(
|
|
||||||
formatExceptionMessage(
|
|
||||||
`Failed to resolve '${property as string}' computed property`,
|
|
||||||
exception as { message: string; stack: string[] }
|
|
||||||
)
|
|
||||||
);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
export function resolveStringBlock(block: any, context: Context, stack: string[]): string {
|
|
||||||
const errorContainer = { message: "", stack };
|
|
||||||
if (!validateStringBlock(block, errorContainer)) {
|
|
||||||
throw errorContainer;
|
|
||||||
}
|
|
||||||
if (typeof block === "string") {
|
|
||||||
return block;
|
|
||||||
}
|
|
||||||
switch (block._type) {
|
|
||||||
case "concat": {
|
|
||||||
const operands = resolveStringArrayBlock(block.operands, context, [
|
|
||||||
...stack,
|
|
||||||
"operands"
|
|
||||||
]);
|
|
||||||
return operands.join("");
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
return resolveReferenceBlock(block, resolveStringBlock, context, stack);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function resolveNumberBlock(block: any, context: Context, stack: string[]): number {
|
|
||||||
const errorContainer = { message: "", stack };
|
|
||||||
if (!validateNumberBlock(block, errorContainer)) {
|
|
||||||
throw errorContainer;
|
|
||||||
}
|
|
||||||
if (typeof block === "number") {
|
|
||||||
return block;
|
|
||||||
}
|
|
||||||
switch (block._type) {
|
|
||||||
case "addition": {
|
|
||||||
const operands = resolveNumberArrayBlock(block.operands, context, [
|
|
||||||
...stack,
|
|
||||||
"operands"
|
|
||||||
]);
|
|
||||||
return operands.reduce((a, b) => a - b);
|
|
||||||
}
|
|
||||||
case "subtraction": {
|
|
||||||
const operands = resolveNumberArrayBlock(block.operands, context, [
|
|
||||||
...stack,
|
|
||||||
"operands"
|
|
||||||
]);
|
|
||||||
return operands.reduce((a, b) => a + b);
|
|
||||||
}
|
|
||||||
case "random": {
|
|
||||||
const max = resolveNumberBlock(block.max, context, [...stack, "max"]);
|
|
||||||
const min = resolveNumberBlock(block.min, context, [...stack, "min"]);
|
|
||||||
return Math.random() * (max - min) + min;
|
|
||||||
}
|
|
||||||
case "randomInt": {
|
|
||||||
const max = resolveNumberBlock(block.max, context, [...stack, "max"]);
|
|
||||||
const min = resolveNumberBlock(block.min, context, [...stack, "min"]);
|
|
||||||
return Math.floor(Math.random() * (max - min) + min);
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
return resolveReferenceBlock(block, resolveNumberBlock, context, stack);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function resolveBooleanBlock(block: any, context: Context, stack: string[]): boolean {
|
|
||||||
const errorContainer = { message: "", stack };
|
|
||||||
if (!validateBooleanBlock(block, errorContainer)) {
|
|
||||||
throw errorContainer;
|
|
||||||
}
|
|
||||||
if (typeof block === "boolean") {
|
|
||||||
return block;
|
|
||||||
}
|
|
||||||
switch (block._type) {
|
|
||||||
case "equals": {
|
|
||||||
const operands = resolveStateArrayBlock(block.operands, context, [
|
|
||||||
...stack,
|
|
||||||
"operands"
|
|
||||||
]);
|
|
||||||
for (let i = 1; i < operands.length; i++) {
|
|
||||||
if (operands[i] != operands[i - 1]) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
case "notEquals": {
|
|
||||||
const operands = resolveStateArrayBlock(block.operands, context, [
|
|
||||||
...stack,
|
|
||||||
"operands"
|
|
||||||
]);
|
|
||||||
for (let i = 1; i < operands.length; i++) {
|
|
||||||
if (operands[i] == operands[i - 1]) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
case "greaterThan": {
|
|
||||||
const operands = resolveNumberArrayBlock(block.operands, context, [
|
|
||||||
...stack,
|
|
||||||
"operands"
|
|
||||||
]);
|
|
||||||
for (let i = 1; i < operands.length; i++) {
|
|
||||||
if (operands[i] <= operands[i - 1]) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
case "greaterThanOrEqual": {
|
|
||||||
const operands = resolveNumberArrayBlock(block.operands, context, [
|
|
||||||
...stack,
|
|
||||||
"operands"
|
|
||||||
]);
|
|
||||||
for (let i = 1; i < operands.length; i++) {
|
|
||||||
if (operands[i] < operands[i - 1]) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
case "lessThan": {
|
|
||||||
const operands = resolveNumberArrayBlock(block.operands, context, [
|
|
||||||
...stack,
|
|
||||||
"operands"
|
|
||||||
]);
|
|
||||||
for (let i = 1; i < operands.length; i++) {
|
|
||||||
if (operands[i] >= operands[i - 1]) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
case "lessThanOrEqual": {
|
|
||||||
const operands = resolveNumberArrayBlock(block.operands, context, [
|
|
||||||
...stack,
|
|
||||||
"operands"
|
|
||||||
]);
|
|
||||||
for (let i = 1; i < operands.length; i++) {
|
|
||||||
if (operands[i] > operands[i - 1]) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
case "contextExists": {
|
|
||||||
const object = resolveStringBlock(block.object, context, [...stack, "object"]);
|
|
||||||
return object in context;
|
|
||||||
}
|
|
||||||
case "propertyExists": {
|
|
||||||
const object = resolveObjectBlock(block.object, context, [...stack, "object"]);
|
|
||||||
const property = resolveStringBlock(block.property, context, [...stack, "property"]);
|
|
||||||
return property in object;
|
|
||||||
}
|
|
||||||
case "all": {
|
|
||||||
const operands = resolveBooleanArrayBlock(block.operands, context, [
|
|
||||||
...stack,
|
|
||||||
"operands"
|
|
||||||
]);
|
|
||||||
return operands.every(value => value);
|
|
||||||
}
|
|
||||||
case "any": {
|
|
||||||
const operands = resolveBooleanArrayBlock(block.operands, context, [
|
|
||||||
...stack,
|
|
||||||
"operands"
|
|
||||||
]);
|
|
||||||
return operands.some(value => value);
|
|
||||||
}
|
|
||||||
case "none": {
|
|
||||||
const operands = resolveBooleanArrayBlock(block.operands, context, [
|
|
||||||
...stack,
|
|
||||||
"operands"
|
|
||||||
]);
|
|
||||||
return !operands.every(value => value);
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
return resolveReferenceBlock(block, resolveBooleanBlock, context, stack);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function resolveObjectBlock(
|
|
||||||
block: any,
|
|
||||||
context: Context,
|
|
||||||
stack: string[]
|
|
||||||
): Record<string, unknown> & { _type: never } {
|
|
||||||
const errorContainer = { message: "", stack };
|
|
||||||
if (!validateObjectBlock(block, errorContainer)) {
|
|
||||||
throw errorContainer;
|
|
||||||
}
|
|
||||||
if ("_type" in block) {
|
|
||||||
return resolveReferenceBlock(block, resolveObjectBlock, context, stack);
|
|
||||||
}
|
|
||||||
return block;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function resolveStateBlock(block: any, context: Context, stack: string[]): any {
|
|
||||||
try {
|
|
||||||
return resolveStringBlock(block, context, stack);
|
|
||||||
} catch {}
|
|
||||||
try {
|
|
||||||
return resolveNumberBlock(block, context, stack);
|
|
||||||
} catch {}
|
|
||||||
try {
|
|
||||||
return resolveBooleanBlock(block, context, stack);
|
|
||||||
} catch {}
|
|
||||||
try {
|
|
||||||
return resolveStateArrayBlock(block, context, stack);
|
|
||||||
} catch {}
|
|
||||||
try {
|
|
||||||
return resolveStateDictionaryBlock(block, context, stack);
|
|
||||||
} catch {}
|
|
||||||
try {
|
|
||||||
return resolveObjectBlock(block, context, stack);
|
|
||||||
} catch {}
|
|
||||||
throw { message: "block could not resolve to any state", stack };
|
|
||||||
}
|
|
||||||
|
|
||||||
export function resolveReferenceBlock<T>(
|
|
||||||
block: any,
|
|
||||||
resolveFunction: (block: any, context: Context, stack: string[]) => T,
|
|
||||||
context: Context,
|
|
||||||
stack: string[]
|
|
||||||
): T {
|
|
||||||
const errorContainer = { message: "", stack };
|
|
||||||
if (!validateReferenceBlock(block, errorContainer)) {
|
|
||||||
throw errorContainer;
|
|
||||||
}
|
|
||||||
switch (block._type) {
|
|
||||||
case "method": {
|
|
||||||
const object = resolveObjectBlock(block.object, context, [
|
|
||||||
...stack,
|
|
||||||
"object"
|
|
||||||
]) as Record<string, unknown>;
|
|
||||||
const method = resolveStringBlock(block.method, context, [...stack, "method"]);
|
|
||||||
const params = resolveStateDictionaryBlock(block.params, context, [...stack, "params"]);
|
|
||||||
let run;
|
|
||||||
if (
|
|
||||||
"_base" in object &&
|
|
||||||
isObject(object["_base"]) &&
|
|
||||||
"methods" in object["_base"] &&
|
|
||||||
isObject(object["_base"].methods) &&
|
|
||||||
method in object["_base"].methods
|
|
||||||
) {
|
|
||||||
run = object["_base"].methods[method].run;
|
|
||||||
} else if (method in object) {
|
|
||||||
run = object[method];
|
|
||||||
} else {
|
|
||||||
errorContainer.message = "Could not run method that does not exist";
|
|
||||||
throw errorContainer;
|
|
||||||
}
|
|
||||||
return resolveFunction(run, { ...context, ...params }, stack);
|
|
||||||
}
|
|
||||||
case "property": {
|
|
||||||
const object = resolveObjectBlock(block.object, context, [...stack, "object"]);
|
|
||||||
const property = resolveStringBlock(block.property, context, [...stack, "property"]);
|
|
||||||
let prop;
|
|
||||||
if (
|
|
||||||
"_base" in object &&
|
|
||||||
isObject(object["_base"]) &&
|
|
||||||
"properties" in object["_base"] &&
|
|
||||||
isObject(object["_base"].properties) &&
|
|
||||||
property in object["_base"].properties
|
|
||||||
) {
|
|
||||||
prop = object["_base"].properties[property].run;
|
|
||||||
} else if (property in object) {
|
|
||||||
prop = object[property];
|
|
||||||
} else {
|
|
||||||
errorContainer.message = `Could not get property ${property} that does not exist`;
|
|
||||||
throw errorContainer;
|
|
||||||
}
|
|
||||||
return resolveFunction(prop, context, stack);
|
|
||||||
}
|
|
||||||
case "getContext": {
|
|
||||||
const id = resolveStringBlock(block.id, context, [...stack, "id"]);
|
|
||||||
if (!(id in context)) {
|
|
||||||
errorContainer.message = `Could not get item ${id} from context that does not exist`;
|
|
||||||
throw errorContainer;
|
|
||||||
}
|
|
||||||
return resolveFunction(context[id], context, stack);
|
|
||||||
}
|
|
||||||
case "ternary": {
|
|
||||||
const condition = resolveBooleanBlock(block.condition, context, [
|
|
||||||
...stack,
|
|
||||||
"condition"
|
|
||||||
]);
|
|
||||||
const prop = condition ? "true" : "false";
|
|
||||||
return resolveStateBlock(block[prop], context, [...stack, prop]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function resolveEntryBlock<T>(
|
|
||||||
resolveFunction: (block: any, context: Context, stack: string[]) => T
|
|
||||||
) {
|
|
||||||
return function (block: any, context: Context, stack: string[]): { key: string; value: T } {
|
|
||||||
const errorContainer = { message: "", stack };
|
|
||||||
if (!validateEntryBlock(block, errorContainer)) {
|
|
||||||
throw errorContainer;
|
|
||||||
}
|
|
||||||
const key = resolveStringBlock(block.key, context, [...stack, "key"]);
|
|
||||||
const value = resolveFunction(block.value, context, [...stack, "value"]);
|
|
||||||
return { key, value };
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function resolveActionBlock(block: any, context: Context, stack: string[]): any {
|
|
||||||
const errorContainer = { message: "", stack };
|
|
||||||
if (!validateActionBlock(block, errorContainer)) {
|
|
||||||
throw errorContainer;
|
|
||||||
}
|
|
||||||
switch (block._type) {
|
|
||||||
case "branch": {
|
|
||||||
const condition = resolveBooleanBlock(block.condition, context, [
|
|
||||||
...stack,
|
|
||||||
"condition"
|
|
||||||
]);
|
|
||||||
if (condition) {
|
|
||||||
if (block.true) {
|
|
||||||
resolveActionArrayBlock(block.true, context, [...stack, "true"]);
|
|
||||||
}
|
|
||||||
} else if (block.false) {
|
|
||||||
resolveActionArrayBlock(block.false, context, [...stack, "false"]);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case "forEach": {
|
|
||||||
const array = resolveStateArrayBlock(block.array, context, [...stack, "array"]);
|
|
||||||
let contextPrefix = "@";
|
|
||||||
let i = 1;
|
|
||||||
while (`${contextPrefix}index` in context || `${contextPrefix}element` in context) {
|
|
||||||
contextPrefix = `${++i}@`;
|
|
||||||
}
|
|
||||||
array.forEach((value, index) => {
|
|
||||||
resolveActionArrayBlock(
|
|
||||||
block.forEach,
|
|
||||||
{
|
|
||||||
...context,
|
|
||||||
[`${contextPrefix}index`]: index,
|
|
||||||
[`${contextPrefix}element`]: value
|
|
||||||
},
|
|
||||||
[...stack, index.toString()]
|
|
||||||
);
|
|
||||||
});
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case "repeat": {
|
|
||||||
const iterations = resolveNumberBlock(block.iterations, context, [
|
|
||||||
...stack,
|
|
||||||
"iterations"
|
|
||||||
]);
|
|
||||||
let contextPrefix = "@";
|
|
||||||
let i = 1;
|
|
||||||
while (`${contextPrefix}iteration` in context) {
|
|
||||||
contextPrefix = `${++i}@`;
|
|
||||||
}
|
|
||||||
for (let i = 0; i < iterations; i++) {
|
|
||||||
resolveActionArrayBlock(
|
|
||||||
block.run,
|
|
||||||
{
|
|
||||||
...context,
|
|
||||||
[`${contextPrefix}iteration`]: i
|
|
||||||
},
|
|
||||||
[...stack, "run"]
|
|
||||||
);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case "wait": {
|
|
||||||
const node = collapseOptionalProperty(block, "node", collapseStringBlock, stack);
|
|
||||||
const duration = collapseProperty(block, "duration", collapseNumberBlock, stack);
|
|
||||||
whitelistProperties(block, "_type", "node", "duration");
|
|
||||||
if (node != null && duration != null) {
|
|
||||||
return block;
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
case "addItemsToInventory": {
|
|
||||||
const node = collapseProperty(block, "node", collapseStringBlock, stack);
|
|
||||||
const items = collapseProperty(block, "items", collapseItemStackArrayBlock, stack);
|
|
||||||
whitelistProperties(block, "_type", "node", "items");
|
|
||||||
if (node != null && items != null) {
|
|
||||||
return block;
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
case "setData": {
|
|
||||||
const object = resolveObjectBlock(block.object, context, [...stack, "object"]);
|
|
||||||
const key = resolveStringBlock(block.key, context, [...stack, "key"]);
|
|
||||||
const value = resolveStateBlock(block.value, context, [...stack, "value"]);
|
|
||||||
object[key] = value;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case "addNode": {
|
|
||||||
const nodeType = resolveStringBlock(block.nodeType, context, [...stack, "nodeType"]);
|
|
||||||
const pos = resolvePositionBlock(block.pos, context, [...stack, "pos"]);
|
|
||||||
const data = resolveStateBlock(block.data, context, [...stack, "data"]);
|
|
||||||
const nodes = main.board.state.value.nodes;
|
|
||||||
nodes.push({
|
|
||||||
id: getUniqueNodeID(main.board),
|
|
||||||
position: pos,
|
|
||||||
type: nodeType,
|
|
||||||
state: data
|
|
||||||
});
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case "removeNode": {
|
|
||||||
const id = resolveNumberBlock(block.node, context, [...stack, "node"]);
|
|
||||||
const nodes = main.board.state.value.nodes;
|
|
||||||
const nodeIndex = nodes.findIndex(node => node.id === id);
|
|
||||||
if (nodeIndex >= 0) {
|
|
||||||
nodes.splice(nodeIndex, 1);
|
|
||||||
} else {
|
|
||||||
throw { message: `could not remove nonexistent node ${id}`, stack };
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case "event": {
|
|
||||||
const event = resolveStringBlock(block.event, context, [...stack, "event"]);
|
|
||||||
const data = resolveStateBlock(block.data, context, [...stack, "data"]);
|
|
||||||
resolveEvent(event, data);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case "error": {
|
|
||||||
const message = resolveStringBlock(block.message, context, [...stack, "message"]);
|
|
||||||
console.error(message);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case "@return": {
|
|
||||||
const value = resolveStateBlock(block.value, context, [...stack, "value"]);
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
case "@break":
|
|
||||||
return BreakAction;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function resolveArrayBlock<T>(
|
|
||||||
resolveFunction: (block: any, context: Context, stack: string[]) => T
|
|
||||||
) {
|
|
||||||
return function (block: any, context: Context, stack: string[]): T[] {
|
|
||||||
const errorContainer = { message: "", stack };
|
|
||||||
if (!validateArrayBlock(block, errorContainer)) {
|
|
||||||
throw errorContainer;
|
|
||||||
}
|
|
||||||
if ("_type" in block) {
|
|
||||||
switch (block._type) {
|
|
||||||
case "filter": {
|
|
||||||
let array = resolveStateArrayBlock(block.array, context, [...stack, "array"]);
|
|
||||||
let contextPrefix = "@";
|
|
||||||
let i = 1;
|
|
||||||
while (
|
|
||||||
`${contextPrefix}index` in context ||
|
|
||||||
`${contextPrefix}element` in context
|
|
||||||
) {
|
|
||||||
contextPrefix = `${++i}@`;
|
|
||||||
}
|
|
||||||
array = array.map((value, index) => {
|
|
||||||
return resolveFunction(
|
|
||||||
block,
|
|
||||||
{
|
|
||||||
...context,
|
|
||||||
[`${contextPrefix}index`]: index,
|
|
||||||
[`${contextPrefix}element`]: value
|
|
||||||
},
|
|
||||||
[...stack, index.toString()]
|
|
||||||
);
|
|
||||||
});
|
|
||||||
for (let i = array.length - 1; i >= 0; i--) {
|
|
||||||
const condition = resolveBooleanBlock(
|
|
||||||
block.condition,
|
|
||||||
{
|
|
||||||
...context,
|
|
||||||
[`${contextPrefix}index`]: i,
|
|
||||||
[`${contextPrefix}element`]: array[i]
|
|
||||||
},
|
|
||||||
[...stack, i.toString()]
|
|
||||||
);
|
|
||||||
if (condition === false) {
|
|
||||||
array.splice(i, 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return array;
|
|
||||||
}
|
|
||||||
case "map": {
|
|
||||||
let array = resolveStateArrayBlock(block.array, context, [...stack, "array"]);
|
|
||||||
let contextPrefix = "@";
|
|
||||||
let i = 1;
|
|
||||||
while (
|
|
||||||
`${contextPrefix}index` in context ||
|
|
||||||
`${contextPrefix}element` in context
|
|
||||||
) {
|
|
||||||
contextPrefix = `${++i}@`;
|
|
||||||
}
|
|
||||||
array = array.map((value, index) => {
|
|
||||||
return resolveStateBlock(
|
|
||||||
block.value,
|
|
||||||
{
|
|
||||||
...context,
|
|
||||||
[`${contextPrefix}index`]: index,
|
|
||||||
[`${contextPrefix}element`]: value
|
|
||||||
},
|
|
||||||
[...stack, index.toString()]
|
|
||||||
);
|
|
||||||
});
|
|
||||||
array = array.map((value, index) => {
|
|
||||||
return resolveFunction(
|
|
||||||
block.value,
|
|
||||||
{
|
|
||||||
...context,
|
|
||||||
[`${contextPrefix}index`]: index,
|
|
||||||
[`${contextPrefix}element`]: value
|
|
||||||
},
|
|
||||||
[...stack, index.toString()]
|
|
||||||
);
|
|
||||||
});
|
|
||||||
return array;
|
|
||||||
}
|
|
||||||
case "keys": {
|
|
||||||
const dictionary = resolveStateDictionaryBlock(block.dictionary, context, [
|
|
||||||
...stack,
|
|
||||||
"dictionary"
|
|
||||||
]);
|
|
||||||
let contextPrefix = "@";
|
|
||||||
let i = 1;
|
|
||||||
while (
|
|
||||||
`${contextPrefix}index` in context ||
|
|
||||||
`${contextPrefix}element` in context
|
|
||||||
) {
|
|
||||||
contextPrefix = `${++i}@`;
|
|
||||||
}
|
|
||||||
const keys = Object.keys(dictionary).map((value, index) => {
|
|
||||||
return resolveFunction(
|
|
||||||
value,
|
|
||||||
{
|
|
||||||
...context,
|
|
||||||
[`${contextPrefix}index`]: index,
|
|
||||||
[`${contextPrefix}element`]: value as any
|
|
||||||
},
|
|
||||||
[...stack, index.toString()]
|
|
||||||
);
|
|
||||||
});
|
|
||||||
return keys as T[];
|
|
||||||
}
|
|
||||||
case "values": {
|
|
||||||
const dictionary = resolveStateDictionaryBlock(block.dictionary, context, [
|
|
||||||
...stack,
|
|
||||||
"dictionary"
|
|
||||||
]);
|
|
||||||
let contextPrefix = "@";
|
|
||||||
let i = 1;
|
|
||||||
while (
|
|
||||||
`${contextPrefix}index` in context ||
|
|
||||||
`${contextPrefix}element` in context
|
|
||||||
) {
|
|
||||||
contextPrefix = `${++i}@`;
|
|
||||||
}
|
|
||||||
const values = Object.values(dictionary).map((value, index) => {
|
|
||||||
return resolveFunction(
|
|
||||||
value,
|
|
||||||
{
|
|
||||||
...context,
|
|
||||||
[`${contextPrefix}index`]: index,
|
|
||||||
[`${contextPrefix}element`]: value
|
|
||||||
},
|
|
||||||
[...stack, index.toString()]
|
|
||||||
);
|
|
||||||
});
|
|
||||||
return values as T[];
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
return resolveReferenceBlock(
|
|
||||||
block,
|
|
||||||
resolveArrayBlock(resolveFunction),
|
|
||||||
context,
|
|
||||||
stack
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let contextPrefix = "@";
|
|
||||||
let i = 1;
|
|
||||||
while (`${contextPrefix}index` in context || `${contextPrefix}element` in context) {
|
|
||||||
contextPrefix = `${++i}@`;
|
|
||||||
}
|
|
||||||
const arr = block.map((value, index) => {
|
|
||||||
return resolveFunction(
|
|
||||||
value,
|
|
||||||
{
|
|
||||||
...context,
|
|
||||||
[`${contextPrefix}index`]: index,
|
|
||||||
[`${contextPrefix}element`]: value
|
|
||||||
},
|
|
||||||
[...stack, index.toString()]
|
|
||||||
);
|
|
||||||
});
|
|
||||||
return arr as T[];
|
|
||||||
};
|
|
||||||
}
|
|
||||||
export const resolveStringArrayBlock = resolveArrayBlock(resolveStringBlock);
|
|
||||||
export const resolveNumberArrayBlock = resolveArrayBlock(resolveNumberBlock);
|
|
||||||
export const resolveBooleanArrayBlock = resolveArrayBlock(resolveBooleanBlock);
|
|
||||||
export const resolveStateArrayBlock = resolveArrayBlock(resolveStateBlock);
|
|
||||||
export const resolveActionArrayBlock = resolveArrayBlock(resolveActionBlock);
|
|
||||||
|
|
||||||
export function resolveDictionaryBlock<T>(
|
|
||||||
resolveFunction: (block: any, context: Context, stack: string[]) => T
|
|
||||||
) {
|
|
||||||
return function (block: any, context: Context, stack: string[]): Record<string, T> {
|
|
||||||
const errorContainer = { message: "", stack };
|
|
||||||
if (!validateDictionaryBlock(block, errorContainer)) {
|
|
||||||
throw errorContainer;
|
|
||||||
}
|
|
||||||
if (!("_type" in block)) {
|
|
||||||
const newBlock = { ...block };
|
|
||||||
Object.keys(newBlock).forEach(key => {
|
|
||||||
newBlock[key] = resolveFunction(newBlock[key], context, [...stack, key]);
|
|
||||||
});
|
|
||||||
return newBlock;
|
|
||||||
}
|
|
||||||
switch (block._type) {
|
|
||||||
case "createDictionary": {
|
|
||||||
const entries = resolveArrayBlock(resolveEntryBlock(resolveFunction))(
|
|
||||||
(block as CreateDictionaryBlock<T>).entries,
|
|
||||||
context,
|
|
||||||
[...stack, "entries"]
|
|
||||||
);
|
|
||||||
return Object.fromEntries(entries.map(e => [e.key, e.value]));
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
return resolveReferenceBlock(
|
|
||||||
block,
|
|
||||||
resolveDictionaryBlock(resolveFunction),
|
|
||||||
context,
|
|
||||||
stack
|
|
||||||
);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
export const resolveStringDictionaryBlock = resolveDictionaryBlock(resolveStringBlock);
|
|
||||||
export const resolveNumberDictionaryBlock = resolveDictionaryBlock(resolveNumberBlock);
|
|
||||||
export const resolveBooleanDictionaryBlock = resolveDictionaryBlock(resolveBooleanBlock);
|
|
||||||
export const resolveStateDictionaryBlock = resolveDictionaryBlock(resolveStateBlock);
|
|
||||||
export const resolveItemStackDictionaryBlock = resolveDictionaryBlock(resolveItemStackBlock);
|
|
||||||
export const resolveNodeActionDictionaryBlock = resolveDictionaryBlock(resolveNodeActionBlock);
|
|
||||||
|
|
||||||
export function resolveInventoryBlock(
|
|
||||||
block: any,
|
|
||||||
context: Context,
|
|
||||||
stack: string[]
|
|
||||||
): { slots: number; canPlayerExtract: boolean; canPlayerInsert: boolean } {
|
|
||||||
const errorContainer = { message: "", stack };
|
|
||||||
if (!validateInventoryBlock(block, errorContainer)) {
|
|
||||||
throw errorContainer;
|
|
||||||
}
|
|
||||||
const slots = resolveNumberBlock(block.slots, context, [...stack, "slots"]);
|
|
||||||
const canPlayerExtract = resolveBooleanBlock(block.canPlayerExtract, context, [
|
|
||||||
...stack,
|
|
||||||
"canPlayerExtract"
|
|
||||||
]);
|
|
||||||
const canPlayerInsert = resolveBooleanBlock(block.canPlayerInsert, context, [
|
|
||||||
...stack,
|
|
||||||
"canPlayerInsert"
|
|
||||||
]);
|
|
||||||
return { slots, canPlayerExtract, canPlayerInsert };
|
|
||||||
}
|
|
||||||
|
|
||||||
export function resolveItemStackBlock(
|
|
||||||
block: any,
|
|
||||||
context: Context,
|
|
||||||
stack: string[]
|
|
||||||
): { item: string; quantity: number } {
|
|
||||||
const errorContainer = { message: "", stack };
|
|
||||||
if (!validateItemStackBlock(block, errorContainer)) {
|
|
||||||
throw errorContainer;
|
|
||||||
}
|
|
||||||
if (typeof block === "object" && "item" in block && "quantity" in block) {
|
|
||||||
const item = resolveStringBlock(block.item, context, [...stack, "item"]);
|
|
||||||
const quantity = resolveNumberBlock(block.quantity, context, [...stack, "quantity"]);
|
|
||||||
return { item, quantity };
|
|
||||||
} else {
|
|
||||||
return resolveReferenceBlock(block, resolveItemStackBlock, context, stack);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function resolveNodeActionBlock(
|
|
||||||
block: any,
|
|
||||||
context: Context,
|
|
||||||
stack: string[]
|
|
||||||
): {
|
|
||||||
display: string;
|
|
||||||
duration: number;
|
|
||||||
tooltip: string;
|
|
||||||
cost: Record<string, { item: string; quantity: number }>;
|
|
||||||
run: ArrayBlock<ActionBlock>;
|
|
||||||
} {
|
|
||||||
const errorContainer = { message: "", stack };
|
|
||||||
if (!validateNodeActionBlock(block, errorContainer)) {
|
|
||||||
throw errorContainer;
|
|
||||||
}
|
|
||||||
const cost = resolveItemStackDictionaryBlock(block.cost, context, [...stack, "cost"]);
|
|
||||||
const display = resolveStringBlock(block.display, context, [...stack, "display"]);
|
|
||||||
const duration = resolveNumberBlock(block.duration, context, [...stack, "duration"]);
|
|
||||||
const tooltip = resolveStringBlock(block.tooltip, context, [...stack, "tooltip"]);
|
|
||||||
return { cost, display, duration, tooltip, run: block.run };
|
|
||||||
}
|
|
||||||
|
|
||||||
export function resolvePositionBlock(
|
|
||||||
block: any,
|
|
||||||
context: Context,
|
|
||||||
stack: string[]
|
|
||||||
): { x: number; y: number } {
|
|
||||||
const errorContainer = { message: "", stack };
|
|
||||||
if (!validatePositionBlock(block, errorContainer)) {
|
|
||||||
throw errorContainer;
|
|
||||||
}
|
|
||||||
if (typeof block === "object" && "x" in block && "y" in block) {
|
|
||||||
const x = resolveNumberBlock(block.x, context, [...stack, "x"]);
|
|
||||||
const y = resolveNumberBlock(block.y, context, [...stack, "y"]);
|
|
||||||
return { x, y };
|
|
||||||
} else {
|
|
||||||
return resolveReferenceBlock(block, resolvePositionBlock, context, stack);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function resolveSizeBlock(
|
|
||||||
block: any,
|
|
||||||
context: Context,
|
|
||||||
stack: string[]
|
|
||||||
): { width: number; height: number } {
|
|
||||||
const errorContainer = { message: "", stack };
|
|
||||||
if (!validateSizeBlock(block, errorContainer)) {
|
|
||||||
throw errorContainer;
|
|
||||||
}
|
|
||||||
if (validateNumberBlock(block, errorContainer)) {
|
|
||||||
const size = resolveNumberBlock(block, context, stack);
|
|
||||||
return { width: size, height: size };
|
|
||||||
} else if (typeof block === "object" && "width" in block && "height" in block) {
|
|
||||||
const width = resolveNumberBlock(block.width, context, [...stack, "width"]);
|
|
||||||
const height = resolveNumberBlock(block.height, context, [...stack, "height"]);
|
|
||||||
return { width, height };
|
|
||||||
} else {
|
|
||||||
errorContainer.message =
|
|
||||||
"Could not resolve size block because it wasn't a number nor size object";
|
|
||||||
throw errorContainer;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,405 +0,0 @@
|
||||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
||||||
import type {
|
|
||||||
ReferenceBlock,
|
|
||||||
DictionaryBlock,
|
|
||||||
EntryBlock,
|
|
||||||
ArrayBlock,
|
|
||||||
StringBlock,
|
|
||||||
NumberBlock,
|
|
||||||
BooleanBlock,
|
|
||||||
ActionBlock,
|
|
||||||
PositionBlock,
|
|
||||||
SizeBlock,
|
|
||||||
Inventory,
|
|
||||||
ItemStackBlock,
|
|
||||||
NodeAction,
|
|
||||||
TypeBlock,
|
|
||||||
MethodTypeBlock,
|
|
||||||
ObjectBlock
|
|
||||||
} from "alkatest-common/types";
|
|
||||||
|
|
||||||
export function validateBlock(
|
|
||||||
block: any,
|
|
||||||
errorContainer: { message: string },
|
|
||||||
skipTypeCheck = false
|
|
||||||
): block is Record<string, unknown> {
|
|
||||||
if (block == null) {
|
|
||||||
errorContainer.message = "block is null";
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (typeof block !== "object") {
|
|
||||||
errorContainer.message = "block is not object";
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (skipTypeCheck) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (!("_type" in block)) {
|
|
||||||
errorContainer.message = "block is missing '_type' property";
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (typeof block._type !== "string") {
|
|
||||||
errorContainer.message = `block has non-string '_type' property`;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function validateProperties<T extends string>(
|
|
||||||
block: object,
|
|
||||||
errorContainer: { message: string },
|
|
||||||
...properties: T[]
|
|
||||||
): block is {
|
|
||||||
[x in T]: x extends T ? unknown : never;
|
|
||||||
} {
|
|
||||||
for (const prop in properties) {
|
|
||||||
if (!(prop in block)) {
|
|
||||||
errorContainer.message = `block is missing '${prop}' property`;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function validateReferenceBlock(
|
|
||||||
block: any,
|
|
||||||
errorContainer: { message: string }
|
|
||||||
): block is ReferenceBlock {
|
|
||||||
if (!validateBlock(block, errorContainer)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
switch (block._type) {
|
|
||||||
case "method":
|
|
||||||
return validateProperties(block, errorContainer, "object", "method");
|
|
||||||
case "property":
|
|
||||||
return validateProperties(block, errorContainer, "object", "property");
|
|
||||||
case "getContext":
|
|
||||||
return validateProperties(block, errorContainer, "id");
|
|
||||||
case "ternary":
|
|
||||||
return validateProperties(block, errorContainer, "condition", "true", "false");
|
|
||||||
default:
|
|
||||||
errorContainer.message = `block has unknown type '${block._type}'`;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function validateDictionaryBlock(
|
|
||||||
block: any,
|
|
||||||
errorContainer: { message: string }
|
|
||||||
): block is DictionaryBlock | ReferenceBlock {
|
|
||||||
if (!validateBlock(block, errorContainer, true)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (block._type == null) {
|
|
||||||
const nullKey = Object.keys(block).find(key => block[key] == null);
|
|
||||||
if (nullKey == null) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
errorContainer.message = `dictionary block has null value for key '${nullKey}`;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (typeof block._type === "string") {
|
|
||||||
switch (block._type) {
|
|
||||||
case "createDictionary":
|
|
||||||
return validateProperties(block, errorContainer, "entries");
|
|
||||||
default:
|
|
||||||
return validateReferenceBlock(block, errorContainer);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function validateEntryBlock(
|
|
||||||
block: any,
|
|
||||||
errorContainer: { message: string }
|
|
||||||
): block is EntryBlock {
|
|
||||||
if (!validateBlock(block, errorContainer, true)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return validateProperties(block, errorContainer, "key", "value");
|
|
||||||
}
|
|
||||||
|
|
||||||
export function validateArrayBlock(
|
|
||||||
block: any,
|
|
||||||
errorContainer: { message: string }
|
|
||||||
): block is ArrayBlock | ReferenceBlock {
|
|
||||||
if (Array.isArray(block)) {
|
|
||||||
const nullIndex = block.findIndex(value => value == null);
|
|
||||||
if (nullIndex == -1) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
errorContainer.message = `array block has null value for index '${nullIndex}`;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (!validateBlock(block, errorContainer)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
switch (block._type) {
|
|
||||||
case "filter":
|
|
||||||
return validateProperties(block, errorContainer, "array", "condition");
|
|
||||||
case "map":
|
|
||||||
return validateProperties(block, errorContainer, "array", "value");
|
|
||||||
case "keys":
|
|
||||||
return validateProperties(block, errorContainer, "dictionary");
|
|
||||||
case "values":
|
|
||||||
return validateProperties(block, errorContainer, "dictionary");
|
|
||||||
default:
|
|
||||||
return validateReferenceBlock(block, errorContainer);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function validateObjectBlock(
|
|
||||||
block: any,
|
|
||||||
errorContainer: { message: string }
|
|
||||||
): block is ObjectBlock {
|
|
||||||
if (!validateBlock(block, errorContainer, true)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if ("_type" in block) {
|
|
||||||
return validateReferenceBlock(block, errorContainer);
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function validateStringBlock(
|
|
||||||
block: any,
|
|
||||||
errorContainer: { message: string }
|
|
||||||
): block is StringBlock | ReferenceBlock {
|
|
||||||
if (typeof block === "string") {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (!validateBlock(block, errorContainer)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
switch (block._type) {
|
|
||||||
case "concat":
|
|
||||||
return validateProperties(block, errorContainer, "operands");
|
|
||||||
default:
|
|
||||||
return validateReferenceBlock(block, errorContainer);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function validateNumberBlock(
|
|
||||||
block: any,
|
|
||||||
errorContainer: { message: string }
|
|
||||||
): block is NumberBlock | ReferenceBlock {
|
|
||||||
if (typeof block === "number") {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (!validateBlock(block, errorContainer)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
switch (block._type) {
|
|
||||||
case "addition":
|
|
||||||
return validateProperties(block, errorContainer, "operands");
|
|
||||||
case "subtraction":
|
|
||||||
return validateProperties(block, errorContainer, "operands");
|
|
||||||
case "random":
|
|
||||||
return validateProperties(block, errorContainer, "min", "max");
|
|
||||||
case "randomInt":
|
|
||||||
return validateProperties(block, errorContainer, "min", "max");
|
|
||||||
default:
|
|
||||||
return validateReferenceBlock(block, errorContainer);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function validateBooleanBlock(
|
|
||||||
block: any,
|
|
||||||
errorContainer: { message: string }
|
|
||||||
): block is BooleanBlock | ReferenceBlock {
|
|
||||||
if (typeof block === "boolean") {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (!validateBlock(block, errorContainer)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
switch (block._type) {
|
|
||||||
case "equals":
|
|
||||||
return validateProperties(block, errorContainer, "operands");
|
|
||||||
case "notEquals":
|
|
||||||
return validateProperties(block, errorContainer, "operands");
|
|
||||||
case "greaterThan":
|
|
||||||
return validateProperties(block, errorContainer, "operands");
|
|
||||||
case "greaterThanOrEqual":
|
|
||||||
return validateProperties(block, errorContainer, "operands");
|
|
||||||
case "lessThan":
|
|
||||||
return validateProperties(block, errorContainer, "operands");
|
|
||||||
case "lessThanOrEqual":
|
|
||||||
return validateProperties(block, errorContainer, "operands");
|
|
||||||
case "contextExists":
|
|
||||||
return validateProperties(block, errorContainer, "object");
|
|
||||||
case "propertyExists":
|
|
||||||
return validateProperties(block, errorContainer, "object", "property");
|
|
||||||
case "all":
|
|
||||||
return validateProperties(block, errorContainer, "operands");
|
|
||||||
case "any":
|
|
||||||
return validateProperties(block, errorContainer, "operands");
|
|
||||||
case "none":
|
|
||||||
return validateProperties(block, errorContainer, "operands");
|
|
||||||
default:
|
|
||||||
return validateReferenceBlock(block, errorContainer);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function validateActionBlock(
|
|
||||||
block: any,
|
|
||||||
errorContainer: { message: string }
|
|
||||||
): block is ActionBlock | ReferenceBlock {
|
|
||||||
if (!validateBlock(block, errorContainer)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
switch (block._type) {
|
|
||||||
case "branch":
|
|
||||||
return validateProperties(block, errorContainer, "condition");
|
|
||||||
case "forEach":
|
|
||||||
return validateProperties(block, errorContainer, "array", "forEach");
|
|
||||||
case "repeat":
|
|
||||||
return validateProperties(block, errorContainer, "iterations", "run");
|
|
||||||
case "wait":
|
|
||||||
return validateProperties(block, errorContainer, "duration");
|
|
||||||
case "addItemsToInventory":
|
|
||||||
return validateProperties(block, errorContainer, "node", "items");
|
|
||||||
case "setData":
|
|
||||||
return validateProperties(block, errorContainer, "object", "key", "value");
|
|
||||||
case "addNode":
|
|
||||||
return validateProperties(block, errorContainer, "nodeType", "pos");
|
|
||||||
case "removeNode":
|
|
||||||
return validateProperties(block, errorContainer, "node");
|
|
||||||
case "event":
|
|
||||||
return validateProperties(block, errorContainer, "event");
|
|
||||||
case "error":
|
|
||||||
return validateProperties(block, errorContainer, "message");
|
|
||||||
case "@return":
|
|
||||||
return true;
|
|
||||||
case "@break":
|
|
||||||
return true;
|
|
||||||
default:
|
|
||||||
return validateReferenceBlock(block, errorContainer);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function validatePositionBlock(
|
|
||||||
block: any,
|
|
||||||
errorContainer: { message: string }
|
|
||||||
): block is PositionBlock | ReferenceBlock {
|
|
||||||
if (!validateBlock(block, errorContainer, true)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if ("_type" in block) {
|
|
||||||
return validateReferenceBlock(block, errorContainer);
|
|
||||||
}
|
|
||||||
return validateProperties(block, errorContainer, "x", "y");
|
|
||||||
}
|
|
||||||
|
|
||||||
export function validateSizeBlock(
|
|
||||||
block: any,
|
|
||||||
errorContainer: { message: string }
|
|
||||||
): block is SizeBlock | ReferenceBlock {
|
|
||||||
if (typeof block === "number") {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (!validateBlock(block, errorContainer, true)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if ("_type" in block) {
|
|
||||||
return (
|
|
||||||
validateNumberBlock(block, errorContainer) ||
|
|
||||||
validateReferenceBlock(block, errorContainer)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return validateProperties(block, errorContainer, "width", "height");
|
|
||||||
}
|
|
||||||
|
|
||||||
export function validateInventoryBlock(
|
|
||||||
block: any,
|
|
||||||
errorContainer: { message: string }
|
|
||||||
): block is Inventory {
|
|
||||||
if (!validateBlock(block, errorContainer, true)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return validateProperties(block, errorContainer, "slots");
|
|
||||||
}
|
|
||||||
|
|
||||||
export function validateItemStackBlock(
|
|
||||||
block: any,
|
|
||||||
errorContainer: { message: string }
|
|
||||||
): block is ItemStackBlock | ReferenceBlock {
|
|
||||||
if (!validateBlock(block, errorContainer, true)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if ("_type" in block) {
|
|
||||||
return validateReferenceBlock(block, errorContainer);
|
|
||||||
}
|
|
||||||
return validateProperties(block, errorContainer, "item", "quantity");
|
|
||||||
}
|
|
||||||
|
|
||||||
export function validateNodeActionBlock(
|
|
||||||
block: any,
|
|
||||||
errorContainer: { message: string }
|
|
||||||
): block is NodeAction {
|
|
||||||
if (!validateBlock(block, errorContainer, true)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return validateProperties(block, errorContainer, "display", "duration", "run");
|
|
||||||
}
|
|
||||||
|
|
||||||
export function validateTypeBlock(
|
|
||||||
block: any,
|
|
||||||
errorContainer: { message: string }
|
|
||||||
): block is TypeBlock {
|
|
||||||
if (typeof block === "string") {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (!validateBlock(block, errorContainer)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
switch (block._type) {
|
|
||||||
case "dictionary":
|
|
||||||
return validateProperties(block, errorContainer, "keyType", "valueType");
|
|
||||||
case "array":
|
|
||||||
return validateProperties(block, errorContainer, "elementType");
|
|
||||||
case "object":
|
|
||||||
return validateProperties(block, errorContainer, "properties");
|
|
||||||
case "number":
|
|
||||||
return true;
|
|
||||||
case "boolean":
|
|
||||||
return true;
|
|
||||||
case "string":
|
|
||||||
return true;
|
|
||||||
case "id":
|
|
||||||
return validateProperties(block, errorContainer, "of");
|
|
||||||
case "itemStack":
|
|
||||||
return true;
|
|
||||||
case "action":
|
|
||||||
return true;
|
|
||||||
default:
|
|
||||||
errorContainer.message = `block has unknown type '${block._type}'`;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function validateMethodTypeBlock(
|
|
||||||
block: any,
|
|
||||||
errorContainer: { message: string }
|
|
||||||
): block is MethodTypeBlock {
|
|
||||||
if (!validateBlock(block, errorContainer)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return validateProperties(block, errorContainer, "run");
|
|
||||||
}
|
|
||||||
|
|
||||||
export function validatePropertyBlock(
|
|
||||||
block: any,
|
|
||||||
errorContainer: { message: string }
|
|
||||||
): block is TypeBlock & { value: any } {
|
|
||||||
if (!validateBlock(block, errorContainer, true)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (!validateProperties(block, errorContainer, "value")) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (!validateTypeBlock(block, errorContainer)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
|
@ -1,467 +0,0 @@
|
||||||
{
|
|
||||||
"display": "Core",
|
|
||||||
"eventListeners": {
|
|
||||||
"newGame": [
|
|
||||||
{
|
|
||||||
"_type": "addNode",
|
|
||||||
"nodeType": "core-tree",
|
|
||||||
"pos": {
|
|
||||||
"x": 0,
|
|
||||||
"y": 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"nodes": {
|
|
||||||
"core-tree": {
|
|
||||||
"display": "🌳",
|
|
||||||
"size": 1,
|
|
||||||
"draggable": false,
|
|
||||||
"data": {
|
|
||||||
"drops": {
|
|
||||||
"_type": "number",
|
|
||||||
"default": {
|
|
||||||
"_type": "randomInt",
|
|
||||||
"min": 4,
|
|
||||||
"max": 8
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"inventory": {
|
|
||||||
"slots": 1,
|
|
||||||
"canPlayerExtract": true,
|
|
||||||
"canPlayerInsert": false
|
|
||||||
},
|
|
||||||
"actions": {
|
|
||||||
"Chop": {
|
|
||||||
"display": "🪓",
|
|
||||||
"duration": 1000,
|
|
||||||
"run": [
|
|
||||||
{
|
|
||||||
"_type": "addItemsToInventory",
|
|
||||||
"node": "@instance",
|
|
||||||
"items": {
|
|
||||||
"_type": "method",
|
|
||||||
"object": "#core-treeLootTable",
|
|
||||||
"method": "roll",
|
|
||||||
"params": {
|
|
||||||
"rolls": 1
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"overflow": "destroy"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"_type": "setData",
|
|
||||||
"object": "@instance",
|
|
||||||
"key": "drops",
|
|
||||||
"value": {
|
|
||||||
"_type": "subtraction",
|
|
||||||
"operands": [
|
|
||||||
{
|
|
||||||
"_type": "property",
|
|
||||||
"object": "@instance",
|
|
||||||
"property": "drops"
|
|
||||||
},
|
|
||||||
1
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"_type": "branch",
|
|
||||||
"condition": {
|
|
||||||
"_type": "equals",
|
|
||||||
"operands": [
|
|
||||||
{
|
|
||||||
"_type": "property",
|
|
||||||
"object": "@instance",
|
|
||||||
"property": "drops"
|
|
||||||
},
|
|
||||||
0
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"true": [
|
|
||||||
{
|
|
||||||
"_type": "removeNode",
|
|
||||||
"node": "@instance"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"core-chest": {
|
|
||||||
"display": "📦",
|
|
||||||
"size": 1,
|
|
||||||
"data": {
|
|
||||||
"tier": "core-woodChestTier"
|
|
||||||
},
|
|
||||||
"inventory": {
|
|
||||||
"slots": {
|
|
||||||
"_type": "property",
|
|
||||||
"object": {
|
|
||||||
"_type": "getContext",
|
|
||||||
"id": {
|
|
||||||
"_type": "property",
|
|
||||||
"object": "@instance",
|
|
||||||
"property": "tier"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"property": "slots"
|
|
||||||
},
|
|
||||||
"canPlayerExtract": true,
|
|
||||||
"canPlayerInsert": true
|
|
||||||
},
|
|
||||||
"actions": {
|
|
||||||
"_type": "property",
|
|
||||||
"object": {
|
|
||||||
"_type": "property",
|
|
||||||
"object": "@instance",
|
|
||||||
"property": "tier"
|
|
||||||
},
|
|
||||||
"property": "actions"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"core-sapling": {
|
|
||||||
"display": "🌱",
|
|
||||||
"size": 1,
|
|
||||||
"draggable": false,
|
|
||||||
"place": [
|
|
||||||
{
|
|
||||||
"_type": "wait",
|
|
||||||
"node": "@instance",
|
|
||||||
"duration": {
|
|
||||||
"_type": "randomInt",
|
|
||||||
"min": 10000,
|
|
||||||
"max": 100000
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"_type": "removeNode",
|
|
||||||
"node": "@instance"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"_type": "addNode",
|
|
||||||
"nodeType": "core-tree",
|
|
||||||
"pos": {
|
|
||||||
"x": {
|
|
||||||
"_type": "property",
|
|
||||||
"object": "@instance",
|
|
||||||
"property": "x"
|
|
||||||
},
|
|
||||||
"y": {
|
|
||||||
"_type": "property",
|
|
||||||
"object": "@instance",
|
|
||||||
"property": "y"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"items": {
|
|
||||||
"core-log": {
|
|
||||||
"display": "🪵"
|
|
||||||
},
|
|
||||||
"core-sapling": {
|
|
||||||
"display": "🌱",
|
|
||||||
"node": "core-sapling"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"types": {
|
|
||||||
"core-weightedLootTable": {
|
|
||||||
"data": {
|
|
||||||
"items": {
|
|
||||||
"_type": "dictionary",
|
|
||||||
"keyType": {
|
|
||||||
"_type": "string"
|
|
||||||
},
|
|
||||||
"valueType": {
|
|
||||||
"_type": "object",
|
|
||||||
"properties": {
|
|
||||||
"weight": {
|
|
||||||
"_type": "number",
|
|
||||||
"default": 1
|
|
||||||
},
|
|
||||||
"item": {
|
|
||||||
"_type": "id",
|
|
||||||
"of": "item"
|
|
||||||
},
|
|
||||||
"quantity": {
|
|
||||||
"_type": "number",
|
|
||||||
"default": 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"currentRollValue": {
|
|
||||||
"_type": "number",
|
|
||||||
"internal": true,
|
|
||||||
"default": 0
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"methods": {
|
|
||||||
"roll": {
|
|
||||||
"params": {
|
|
||||||
"rolls": {
|
|
||||||
"_type": "number",
|
|
||||||
"default": 1
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"returns": {
|
|
||||||
"_type": "itemStack"
|
|
||||||
},
|
|
||||||
"run": [
|
|
||||||
{
|
|
||||||
"_type": "repeat",
|
|
||||||
"iterations": {
|
|
||||||
"_type": "property",
|
|
||||||
"object": "@params",
|
|
||||||
"property": "rolls"
|
|
||||||
},
|
|
||||||
"run": [
|
|
||||||
{
|
|
||||||
"_type": "setData",
|
|
||||||
"object": "@instance",
|
|
||||||
"key": "currentRollValue",
|
|
||||||
"value": {
|
|
||||||
"_type": "random",
|
|
||||||
"min": 0,
|
|
||||||
"max": {
|
|
||||||
"_type": "addition",
|
|
||||||
"operands": {
|
|
||||||
"_type": "map",
|
|
||||||
"array": {
|
|
||||||
"_type": "values",
|
|
||||||
"dictionary": {
|
|
||||||
"_type": "property",
|
|
||||||
"object": "@instance",
|
|
||||||
"property": "items"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"value": {
|
|
||||||
"_type": "property",
|
|
||||||
"object": "@element",
|
|
||||||
"property": "weight"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"_type": "forEach",
|
|
||||||
"array": {
|
|
||||||
"_type": "values",
|
|
||||||
"dictionary": {
|
|
||||||
"_type": "property",
|
|
||||||
"object": "@instance",
|
|
||||||
"property": "items"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"forEach": [
|
|
||||||
{
|
|
||||||
"_type": "setData",
|
|
||||||
"object": "@instance",
|
|
||||||
"key": "currentRollValue",
|
|
||||||
"value": {
|
|
||||||
"_type": "subtraction",
|
|
||||||
"operands": [
|
|
||||||
{
|
|
||||||
"_type": "property",
|
|
||||||
"object": "@instance",
|
|
||||||
"property": "currentRollValue"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"_type": "property",
|
|
||||||
"object": "@element",
|
|
||||||
"property": "weight"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"_type": "branch",
|
|
||||||
"condition": {
|
|
||||||
"_type": "lessThanOrEqual",
|
|
||||||
"operands": [
|
|
||||||
{
|
|
||||||
"_type": "property",
|
|
||||||
"object": "@instance",
|
|
||||||
"property": "currentRollValue"
|
|
||||||
},
|
|
||||||
0
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"true": [
|
|
||||||
{
|
|
||||||
"_type": "@return",
|
|
||||||
"value": "@element"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"_type": "error",
|
|
||||||
"message": "Failed to roll loot table"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"_type": "@return",
|
|
||||||
"value": "null"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"core-chestTier": {
|
|
||||||
"data": {
|
|
||||||
"upgradesFrom": {
|
|
||||||
"_type": "dictionary",
|
|
||||||
"keyType": {
|
|
||||||
"_type": "id",
|
|
||||||
"of": "core-chestTier"
|
|
||||||
},
|
|
||||||
"valueType": {
|
|
||||||
"_type": "object",
|
|
||||||
"properties": {
|
|
||||||
"cost": {
|
|
||||||
"_type": "array",
|
|
||||||
"elementType": {
|
|
||||||
"_type": "object",
|
|
||||||
"properties": {
|
|
||||||
"item": {
|
|
||||||
"_type": "id",
|
|
||||||
"of": "item"
|
|
||||||
},
|
|
||||||
"quantity": {
|
|
||||||
"_type": "number"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"slots": {
|
|
||||||
"_type": "number"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"properties": {
|
|
||||||
"actions": {
|
|
||||||
"_type": "dictionary",
|
|
||||||
"keyType": {
|
|
||||||
"_type": "id",
|
|
||||||
"of": "core-chestTier"
|
|
||||||
},
|
|
||||||
"valueType": {
|
|
||||||
"_type": "action"
|
|
||||||
},
|
|
||||||
"value": {
|
|
||||||
"_type": "createDictionary",
|
|
||||||
"entries": {
|
|
||||||
"_type": "map",
|
|
||||||
"array": {
|
|
||||||
"_type": "filter",
|
|
||||||
"array": {
|
|
||||||
"_type": "values",
|
|
||||||
"dictionary": {
|
|
||||||
"_type": "getAllOfType",
|
|
||||||
"of": "core-chestTier"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"condition": {
|
|
||||||
"_type": "contains",
|
|
||||||
"array": {
|
|
||||||
"_type": "keys",
|
|
||||||
"dictionary": {
|
|
||||||
"_type": "property",
|
|
||||||
"object": "@element",
|
|
||||||
"property": "upgradesFrom"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"value": {
|
|
||||||
"_type": "property",
|
|
||||||
"object": "@instance",
|
|
||||||
"property": "id"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"value": {
|
|
||||||
"key": {
|
|
||||||
"_type": "property",
|
|
||||||
"object": "@element",
|
|
||||||
"property": "id"
|
|
||||||
},
|
|
||||||
"value": {
|
|
||||||
"_type": "action",
|
|
||||||
"display": "⇪",
|
|
||||||
"tooltip": {
|
|
||||||
"_type": "concat",
|
|
||||||
"operands": [
|
|
||||||
"Upgrade to ",
|
|
||||||
{
|
|
||||||
"_type": "property",
|
|
||||||
"object": "@element",
|
|
||||||
"property": "display"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"cost": {
|
|
||||||
"_type": "property",
|
|
||||||
"object": {
|
|
||||||
"_type": "property",
|
|
||||||
"object": {
|
|
||||||
"_type": "property",
|
|
||||||
"object": "@element",
|
|
||||||
"property": "upgradesFrom"
|
|
||||||
},
|
|
||||||
"property": {
|
|
||||||
"_type": "property",
|
|
||||||
"object": "@instance",
|
|
||||||
"property": "id"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"property": "cost"
|
|
||||||
},
|
|
||||||
"run": [
|
|
||||||
{
|
|
||||||
"_type": "setData",
|
|
||||||
"object": "@instance",
|
|
||||||
"key": "tier",
|
|
||||||
"value": {
|
|
||||||
"_type": "property",
|
|
||||||
"object": "@element",
|
|
||||||
"property": "id"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"core-weightedLootTable": {
|
|
||||||
"core-treeLootTable": {
|
|
||||||
"items": {
|
|
||||||
"singleLog": {
|
|
||||||
"item": "core-log"
|
|
||||||
},
|
|
||||||
"singleSapling": {
|
|
||||||
"item": "core-sapling"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"core-chestTier": {
|
|
||||||
"core-woodChestTier": {
|
|
||||||
"display": "Wood Chest",
|
|
||||||
"upgradesFrom": {},
|
|
||||||
"slots": 4
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
26
src/data/main.css
Normal file
26
src/data/main.css
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
.main::before {
|
||||||
|
content: "";
|
||||||
|
position: fixed;
|
||||||
|
top: -100vmax;
|
||||||
|
left: -100vmax;
|
||||||
|
right: -100vmax;
|
||||||
|
bottom: -100vmax;
|
||||||
|
opacity: 0.25;
|
||||||
|
background:
|
||||||
|
linear-gradient(45deg, #81A1C1 25%, transparent 25%) -100px 0,
|
||||||
|
linear-gradient(45deg, transparent 75%, #81A1C1 75%) -100px 0,
|
||||||
|
linear-gradient(135deg, #88C0D0 25%, transparent 25%),
|
||||||
|
linear-gradient(135deg, transparent 75%, #88C0D0 75%);
|
||||||
|
background-size: 200px 200px;
|
||||||
|
background-color: #2E3440;
|
||||||
|
animation: moveBackground 30s linear infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes moveBackground {
|
||||||
|
0% {
|
||||||
|
transform: translate(0, 0);
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
transform: translate(200px, 200px);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,104 +1,43 @@
|
||||||
import type {
|
|
||||||
ActionBlock,
|
|
||||||
ArrayBlock,
|
|
||||||
ContentPack,
|
|
||||||
NodeType,
|
|
||||||
ItemType,
|
|
||||||
TypeType
|
|
||||||
} from "alkatest-common/types";
|
|
||||||
import { createBoard, Shape } from "features/boards/board";
|
|
||||||
import { jsx } from "features/feature";
|
import { jsx } from "features/feature";
|
||||||
import { globalBus } from "game/events";
|
|
||||||
import type { BaseLayer, GenericLayer } from "game/layers";
|
import type { BaseLayer, GenericLayer } from "game/layers";
|
||||||
import { createLayer } from "game/layers";
|
import { createLayer } from "game/layers";
|
||||||
import { persistent } from "game/persistence";
|
|
||||||
import type { PlayerData } from "game/player";
|
import type { PlayerData } from "game/player";
|
||||||
import { render } from "util/vue";
|
import { computed } from "vue";
|
||||||
import { computed, ref, watch } from "vue";
|
|
||||||
import Chat from "./Chat.vue";
|
import Chat from "./Chat.vue";
|
||||||
import { processContentPacks } from "./contentPackLoader";
|
import HexGrid from "./HexGrid.vue";
|
||||||
import { Context, run } from "./contentPackResolver";
|
import { persistent } from "game/persistence";
|
||||||
import core from "./contentPacks/core.json";
|
import "./main.css";
|
||||||
import { emit } from "./socket";
|
|
||||||
|
|
||||||
const knownContentPacks: Record<string, ContentPack> = {
|
const tiles = {
|
||||||
core
|
gray: "rgba(64, 64, 64, 0.6)",
|
||||||
|
red: "rgba(255, 0, 0, 0.86)",
|
||||||
|
green: "rgba(0, 255, 0, 0.6)",
|
||||||
|
blue: "rgba(0, 0, 255, 0.6)"
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @hidden
|
* @hidden
|
||||||
*/
|
*/
|
||||||
export const main = createLayer("main", function (this: BaseLayer) {
|
export const main = createLayer("main", function (this: BaseLayer) {
|
||||||
const contentPacks = persistent<(ContentPack | string)[]>(["core"]);
|
const grid = persistent([
|
||||||
const newGame = persistent<boolean>(true);
|
[tiles.gray, tiles.gray],
|
||||||
|
[tiles.gray, tiles.gray, tiles.gray],
|
||||||
const itemTypes = ref<Record<string, ItemType>>({});
|
[tiles.gray, tiles.gray]
|
||||||
const nodeTypes = ref<Record<string, NodeType>>({});
|
]);
|
||||||
const customTypes = ref<Record<string, TypeType>>({});
|
|
||||||
const customObjects = ref<Context>({});
|
|
||||||
const events = ref<Record<string, ArrayBlock<ActionBlock>[]>>({});
|
|
||||||
watch(contentPacks, contentPacks => {
|
|
||||||
const { items, nodes, types, objects, eventListeners } = processContentPacks(
|
|
||||||
contentPacks.map(pack => (typeof pack === "string" ? knownContentPacks[pack] : pack))
|
|
||||||
);
|
|
||||||
console.log(items, nodes, types, objects, eventListeners);
|
|
||||||
itemTypes.value = items;
|
|
||||||
nodeTypes.value = nodes;
|
|
||||||
customTypes.value = types;
|
|
||||||
customObjects.value = objects;
|
|
||||||
events.value = eventListeners;
|
|
||||||
});
|
|
||||||
|
|
||||||
const board = createBoard(() => ({
|
|
||||||
startNodes: () => [],
|
|
||||||
types: {
|
|
||||||
placeholder: {
|
|
||||||
shape: Shape.Diamond,
|
|
||||||
size: 10,
|
|
||||||
title: "placeholder"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
width: "calc(100% + 20px)",
|
|
||||||
height: "calc(100% + 100px)",
|
|
||||||
style: "margin-top: -50px; margin-left: -10px; overflow: hidden"
|
|
||||||
}));
|
|
||||||
|
|
||||||
const position = ref<{ x: number; y: number }>({ x: 0, y: 0 });
|
|
||||||
setInterval(() => {
|
|
||||||
const pos = board.mousePosition.value;
|
|
||||||
if (pos && (pos.x !== position.value.x || pos.y !== position.value.y)) {
|
|
||||||
position.value = pos;
|
|
||||||
emit("set cursor position", pos);
|
|
||||||
}
|
|
||||||
}, 50);
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
name: "Main",
|
name: "Main",
|
||||||
newGame,
|
|
||||||
minimizable: false,
|
minimizable: false,
|
||||||
contentPacks,
|
classes: { main: true },
|
||||||
itemTypes,
|
grid,
|
||||||
nodeTypes,
|
|
||||||
customTypes,
|
|
||||||
customObjects,
|
|
||||||
events,
|
|
||||||
board,
|
|
||||||
display: jsx(() => (
|
display: jsx(() => (
|
||||||
<>
|
<>
|
||||||
{render(board)}
|
<HexGrid gridData={grid.value} selectedColor={tiles.red} />
|
||||||
<Chat />
|
<Chat />
|
||||||
</>
|
</>
|
||||||
))
|
))
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
globalBus.on("onLoad", () => {
|
|
||||||
if (main.newGame.value) {
|
|
||||||
main.events.value.newGame.forEach(actions => run(actions));
|
|
||||||
// main.newGame.value = false;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Given a player save data object being loaded, return a list of layers that should currently be enabled.
|
* Given a player save data object being loaded, return a list of layers that should currently be enabled.
|
||||||
* If your project does not use dynamic layers, this should just return all layers.
|
* If your project does not use dynamic layers, this should just return all layers.
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"title": "Alkatest",
|
"title": "Chromatic Latice",
|
||||||
"description": "A test",
|
"description": "A multiplayer game about light and hexagons",
|
||||||
"id": "alkatest",
|
"id": "chromatic",
|
||||||
"author": "thepaperpilot",
|
"author": "thepaperpilot",
|
||||||
"discordName": "",
|
"discordName": "",
|
||||||
"discordLink": "",
|
"discordLink": "",
|
||||||
|
|
|
@ -17,7 +17,6 @@ import {
|
||||||
GameState,
|
GameState,
|
||||||
ServerToClientEvents
|
ServerToClientEvents
|
||||||
} from "alkatest-common/types";
|
} from "alkatest-common/types";
|
||||||
import { main } from "./projEntry";
|
|
||||||
|
|
||||||
export const connected = ref<boolean>(false);
|
export const connected = ref<boolean>(false);
|
||||||
export const room = ref<string | null>(null);
|
export const room = ref<string | null>(null);
|
||||||
|
@ -161,14 +160,6 @@ function setupSocket(socket: Socket<ServerToClientEvents, ClientToServerEvents>)
|
||||||
socket.on("set nicknames", n => {
|
socket.on("set nicknames", n => {
|
||||||
nicknames.value = n;
|
nicknames.value = n;
|
||||||
});
|
});
|
||||||
socket.on("set cursor position", (id, pos) => {
|
|
||||||
if (id !== socket.id) {
|
|
||||||
cursorPositions.value[id] = pos;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
socket.on("set content packs", contentPacks => {
|
|
||||||
main.contentPacks.value = contentPacks;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function randomName(): string {
|
function randomName(): string {
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import type { CoercableComponent, OptionsFunc, Replace, StyleValue } from "features/feature";
|
import type { CoercableComponent, GenericComponent, OptionsFunc, Replace, StyleValue } from "features/feature";
|
||||||
import { Component, GatherProps, getUniqueID, setDefault, Visibility } from "features/feature";
|
import { Component, GatherProps, getUniqueID, setDefault, Visibility } from "features/feature";
|
||||||
import TabButtonComponent from "features/tabs/TabButton.vue";
|
import TabButtonComponent from "features/tabs/TabButton.vue";
|
||||||
import TabFamilyComponent from "features/tabs/TabFamily.vue";
|
import TabFamilyComponent from "features/tabs/TabFamily.vue";
|
||||||
|
@ -66,7 +66,7 @@ export interface BaseTabFamily {
|
||||||
activeTab: Ref<GenericTab | CoercableComponent | null>;
|
activeTab: Ref<GenericTab | CoercableComponent | null>;
|
||||||
selected: Persistent<string>;
|
selected: Persistent<string>;
|
||||||
type: typeof TabFamilyType;
|
type: typeof TabFamilyType;
|
||||||
[Component]: typeof TabFamilyComponent;
|
[Component]: GenericComponent;
|
||||||
[GatherProps]: () => Record<string, unknown>;
|
[GatherProps]: () => Record<string, unknown>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -100,7 +100,7 @@ export function createTabFamily<T extends TabFamilyOptions>(
|
||||||
|
|
||||||
tabFamily.id = getUniqueID("tabFamily-");
|
tabFamily.id = getUniqueID("tabFamily-");
|
||||||
tabFamily.type = TabFamilyType;
|
tabFamily.type = TabFamilyType;
|
||||||
tabFamily[Component] = TabFamilyComponent;
|
tabFamily[Component] = TabFamilyComponent as GenericComponent;
|
||||||
|
|
||||||
tabFamily.tabs = Object.keys(tabs).reduce<Record<string, GenericTabButton>>(
|
tabFamily.tabs = Object.keys(tabs).reduce<Record<string, GenericTabButton>>(
|
||||||
(parsedTabs, tab) => {
|
(parsedTabs, tab) => {
|
||||||
|
|
Loading…
Reference in a new issue