Implement reading content packs

This commit is contained in:
thepaperpilot 2022-10-14 07:44:30 -05:00
parent a356c3f676
commit 49e10cf858
9 changed files with 1849 additions and 155 deletions

View file

@ -39,6 +39,7 @@ import { ref, toRefs, watch } from "vue";
import Text from "components/fields/Text.vue";
import { Direction } from "util/common";
import Tooltip from "features/tooltips/Tooltip.vue";
import { ClientRoomData } from "alkatest-common/types";
const _props = defineProps<{
isPrivate: boolean;

View file

@ -60,6 +60,7 @@
</template>
<script setup lang="tsx">
import type { ClientRoomData } from "alkatest-common/types";
import Text from "components/fields/Text.vue";
import Toggle from "components/fields/Toggle.vue";
import Modal from "components/Modal.vue";

File diff suppressed because it is too large Load diff

View file

View file

@ -0,0 +1,282 @@
import type {
ReferenceBlock,
DictionaryBlock,
EntryBlock,
ArrayBlock,
StringBlock,
NumberBlock,
BooleanBlock,
ActionBlock,
PositionBlock,
SizeBlock,
Inventory,
ItemStackBlock,
NodeAction,
TypeBlock,
MethodTypeBlock,
StateBlock
} from "alkatest-common/types";
export function validateReferenceBlock(block: ReferenceBlock) {
if (typeof block !== "object" || block == null) {
return false;
}
switch (block._type) {
case "method":
return "object" in block && "method" in block;
case "property":
return "object" in block && "property" in block;
case "getObject":
return "id" in block;
case "ternary":
return "condition" in block && "true" in block && "false" in block;
default:
return false;
}
}
export function validateDictionaryBlock<T>(block: DictionaryBlock<T>): boolean {
if (typeof block !== "object" || block == null) {
return false;
}
if (block._type == null) {
return (Object.values(block) as T[]).every(value => value != null);
}
if (typeof block._type === "string") {
switch (block._type) {
case "createDictionary":
return "entries" in block;
default:
return validateReferenceBlock(block as ReferenceBlock);
}
}
return false;
}
export function validateEntryBlock<T>(block: EntryBlock<T>): boolean {
if (typeof block !== "object" || block == null) {
return false;
}
if (block._type !== "entry") {
return false;
}
return "key" in block && "value" in block;
}
export function validateArrayBlock<T>(block: ArrayBlock<T>): boolean {
if (typeof block !== "object" || block == null) {
return false;
}
if ("_type" in block) {
switch (block._type) {
case "filter":
return "array" in block && "condition" in block;
case "map":
return "array" in block && "value" in block;
case "keys":
return "dictionary" in block;
case "values":
return "dictionary" in block;
default:
return validateReferenceBlock(block);
}
}
return block.every(value => value != null);
}
export function validateStringBlock(block: StringBlock): boolean {
if (typeof block === "string") {
return true;
}
if (typeof block !== "object" || block == null) {
return false;
}
switch (block._type) {
case "concat":
return "operands" in block;
default:
return validateReferenceBlock(block);
}
}
export function validateNumberBlock(block: NumberBlock): boolean {
if (typeof block === "number") {
return true;
}
if (typeof block !== "object" || block == null) {
return false;
}
switch (block._type) {
case "addition":
return "operands" in block;
case "subtraction":
return "operands" in block;
case "random":
return "min" in block && "max" in block;
case "randomInt":
return "min" in block && "max" in block;
default:
return validateReferenceBlock(block);
}
}
export function validateBooleanBlock(block: BooleanBlock): boolean {
if (typeof block === "boolean") {
return true;
}
if (typeof block !== "object" || block == null) {
return false;
}
switch (block._type) {
case "equals":
return "operands" in block;
case "notEquals":
return "operands" in block;
case "greaterThan":
return "operands" in block;
case "greaterThanOrEqual":
return "operands" in block;
case "lessThan":
return "operands" in block;
case "lessThanOrEqual":
return "operands" in block;
case "objectExists":
return "operands" in block;
case "propertyExists":
return "operands" in block && "property" in block;
default:
return validateReferenceBlock(block);
}
}
export function validateActionBlock(block: ActionBlock): boolean {
if (typeof block !== "object" || block == null) {
return false;
}
switch (block._type) {
case "branch":
return "condition" in block;
case "forEach":
return "array" in block && "forEach" in block;
case "repeat":
return "iterations" in block && "run" in block;
case "wait":
return "duration" in block;
case "addItemsToInventory":
return "node" in block && "items" in block;
case "setData":
return "object" in block && "key" in block && "value" in block;
case "addNode":
return "nodeType" in block && "pos" in block;
case "removeNode":
return "node" in block;
case "event":
return "event" in block;
case "error":
return "message" in block;
case "@return":
return true;
case "@break":
return true;
default:
return validateReferenceBlock(block);
}
}
export function validatePositionBlock(block: PositionBlock): boolean {
if (typeof block !== "object" || block == null) {
return false;
}
if ("type" in block) {
return validateReferenceBlock(block);
}
return "x" in block && "y" in block;
}
export function validateSizeBlock(block: SizeBlock): boolean {
if (typeof block === "number") {
return true;
}
if (typeof block !== "object" || block == null) {
return false;
}
if ("type" in block) {
return validateNumberBlock(block) || validateReferenceBlock(block as ReferenceBlock);
}
return "width" in block && "height" in block;
}
export function validateInventoryBlock(block: Inventory): boolean {
if (typeof block !== "object" || block == null) {
return false;
}
return "slots" in block;
}
export function validateItemStackBlock(block: ItemStackBlock): boolean {
if (typeof block !== "object" || block == null) {
return false;
}
if ("item" in block && "quantity" in block) {
return true;
}
return validateReferenceBlock(block);
}
export function validateNodeActionBlock(block: NodeAction): boolean {
if (typeof block !== "object" || block == null) {
return false;
}
return "display" in block && "duration" in block && "run" in block;
}
export function validateTypeBlock(block: TypeBlock): boolean {
if (typeof block === "string") {
return true;
}
if (typeof block !== "object" || block == null) {
return false;
}
switch (block._type) {
case "dictionary":
return "keyType" in block && "valueType" in block;
case "array":
return "elementType" in block;
case "object":
return "properties" in block;
case "number":
return true;
case "boolean":
return true;
case "string":
return true;
case "id":
return "of" in block;
case "itemStack":
return true;
case "action":
return true;
default:
return false;
}
}
export function validateMethodTypeBlock(block: MethodTypeBlock): boolean {
if (typeof block !== "object" || block == null) {
return false;
}
return "run" in block;
}
export function validatePropertyBlock(block: TypeBlock & { value: StateBlock }): boolean {
if (typeof block !== "object" || block == null) {
return false;
}
if (!("value" in block)) {
return false;
}
if (!validateTypeBlock(block)) {
return false;
}
return true;
}

View file

@ -1,13 +1,17 @@
{
"startingNodes": [
"display": "Core",
"eventListeners": {
"newGame": [
{
"type": "core-tree",
"_type": "addNode",
"nodeType": "core-tree",
"pos": {
"x": 0,
"y": 0
}
}
],
]
},
"nodes": {
"core-tree": {
"display": "🌳",
@ -15,10 +19,13 @@
"draggable": false,
"data": {
"drops": {
"type": "randomInt",
"_type": "number",
"default": {
"_type": "randomInt",
"min": 4,
"max": 8
}
}
},
"inventory": {
"slots": 1,
@ -31,10 +38,10 @@
"duration": 1000,
"run": [
{
"type": "addItemsToInventory",
"_type": "addItemsToInventory",
"node": "@instance",
"items": {
"type": "method",
"_type": "method",
"object": "#core-treeLootTable",
"method": "roll",
"params": {
@ -44,14 +51,14 @@
"overflow": "destroy"
},
{
"type": "setData",
"_type": "setData",
"object": "@instance",
"key": "drops",
"value": {
"type": "subtraction",
"_type": "subtraction",
"operands": [
{
"type": "property",
"_type": "property",
"object": "@instance",
"property": "drops"
},
@ -60,12 +67,12 @@
}
},
{
"type": "branch",
"_type": "branch",
"condition": {
"type": "equals",
"_type": "equals",
"operands": [
{
"type": "property",
"_type": "property",
"object": "@instance",
"property": "drops"
},
@ -74,7 +81,7 @@
},
"true": [
{
"type": "removeNode",
"_type": "removeNode",
"node": "@instance"
}
]
@ -91,11 +98,11 @@
},
"inventory": {
"slots": {
"type": "property",
"_type": "property",
"object": {
"type": "getObject",
"_type": "getObject",
"id": {
"type": "property",
"_type": "property",
"object": "@instance",
"property": "tier"
}
@ -106,9 +113,9 @@
"canPlayerInsert": true
},
"actions": {
"type": "property",
"_type": "property",
"object": {
"type": "property",
"_type": "property",
"object": "@instance",
"property": "tier"
},
@ -121,29 +128,29 @@
"draggable": false,
"place": [
{
"type": "wait",
"_type": "wait",
"node": "@instance",
"duration": {
"type": "randomInt",
"_type": "randomInt",
"min": 10000,
"max": 100000
}
},
{
"type": "removeNode",
"_type": "removeNode",
"node": "@instance"
},
{
"type": "addNode",
"_type": "addNode",
"nodeType": "core-tree",
"pos": {
"x": {
"type": "property",
"_type": "property",
"object": "@instance",
"property": "x"
},
"y": {
"type": "property",
"_type": "property",
"object": "@instance",
"property": "y"
}
@ -165,66 +172,75 @@
"core-weightedLootTable": {
"data": {
"items": {
"type": "dictionary",
"_type": "dictionary",
"keyType": {
"type": "string"
"_type": "string"
},
"valueType": {
"type": "object",
"_type": "object",
"properties": {
"weight": {
"type": "number",
"_type": "number",
"default": 1
},
"item": {
"type": "id",
"_type": "id",
"of": "item"
},
"quantity": {
"type": "number",
"_type": "number",
"default": 1
}
}
}
},
"currentRollValue": {
"type": "number",
"internal": true
"_type": "number",
"internal": true,
"default": 0
}
},
"methods": {
"roll": {
"params": {
"rolls": {
"type": "number",
"_type": "number",
"default": 1
}
},
"returns": {
"type": "item"
"_type": "itemStack"
},
"run": [
{
"type": "setData",
"_type": "repeat",
"iterations": {
"_type": "property",
"object": "@params",
"property": "rolls"
},
"run": [
{
"_type": "setData",
"object": "@instance",
"key": "currentRollValue",
"value": {
"type": "random",
"_type": "random",
"min": 0,
"max": {
"type": "addition",
"_type": "addition",
"operands": {
"type": "map",
"_type": "map",
"array": {
"type": "values",
"_type": "values",
"dictionary": {
"type": "property",
"_type": "property",
"object": "@instance",
"property": "items"
}
},
"output": {
"type": "property",
"value": {
"_type": "property",
"object": "@element",
"property": "weight"
}
@ -233,30 +249,30 @@
}
},
{
"type": "forEach",
"_type": "forEach",
"array": {
"type": "values",
"_type": "values",
"dictionary": {
"type": "property",
"_type": "property",
"object": "@instance",
"property": "items"
}
},
"forEach": [
{
"type": "setData",
"_type": "setData",
"object": "@instance",
"key": "currentRollValue",
"value": {
"type": "subtraction",
"_type": "subtraction",
"operands": [
{
"type": "property",
"_type": "property",
"object": "@instance",
"property": "currentRollValue"
},
{
"type": "property",
"_type": "property",
"object": "@element",
"property": "weight"
}
@ -264,12 +280,12 @@
}
},
{
"type": "branch",
"_type": "branch",
"condition": {
"type": "lessThanOrEqual",
"_type": "lessThanOrEqual",
"operands": [
{
"type": "property",
"_type": "property",
"object": "@instance",
"property": "currentRollValue"
},
@ -278,7 +294,7 @@
},
"true": [
{
"type": "@return",
"_type": "@return",
"value": "@element"
}
]
@ -286,39 +302,41 @@
]
},
{
"type": "error",
"_type": "error",
"message": "Failed to roll loot table"
},
{
"type": "@return",
"_type": "@return",
"value": "null"
}
]
}
]
}
}
},
"core-chestTier": {
"data": {
"upgradesFrom": {
"type": "dictionary",
"_type": "dictionary",
"keyType": {
"type": "id",
"_type": "id",
"of": "core-chestTier"
},
"valueType": {
"type": "object",
"_type": "object",
"properties": {
"cost": {
"type": "array",
"_type": "array",
"elementType": {
"type": "object",
"_type": "object",
"properties": {
"item": {
"type": "id",
"_type": "id",
"of": "item"
},
"quantity": {
"type": "number"
"_type": "number"
}
}
}
@ -327,81 +345,81 @@
}
},
"slots": {
"type": "number"
"_type": "number"
}
},
"properties": {
"actions": {
"type": "dictionary",
"_type": "dictionary",
"keyType": {
"type": "id",
"_type": "id",
"of": "core-chestTier"
},
"valueType": {
"type": "action"
"_type": "action"
},
"value": {
"type": "createDictionary",
"_type": "createDictionary",
"entries": {
"type": "map",
"_type": "map",
"array": {
"type": "filter",
"_type": "filter",
"array": {
"type": "values",
"_type": "values",
"dictionary": {
"type": "getAllOfType",
"_type": "getAllOfType",
"of": "core-chestTier"
}
},
"condition": {
"type": "contains",
"_type": "contains",
"array": {
"type": "keys",
"_type": "keys",
"dictionary": {
"type": "property",
"_type": "property",
"object": "@element",
"property": "upgradesFrom"
}
},
"value": {
"type": "property",
"_type": "property",
"object": "@instance",
"property": "id"
}
}
},
"output": {
"type": "entry",
"value": {
"_type": "entry",
"key": {
"type": "property",
"_type": "property",
"object": "@element",
"property": "id"
},
"value": {
"type": "action",
"icon": "⇪",
"_type": "action",
"display": "⇪",
"tooltip": {
"type": "concat",
"_type": "concat",
"operands": [
"Upgrade to ",
{
"type": "property",
"_type": "property",
"object": "@element",
"property": "display"
}
]
},
"cost": {
"type": "property",
"_type": "property",
"object": {
"type": "property",
"_type": "property",
"object": {
"type": "property",
"_type": "property",
"object": "@element",
"property": "upgradesFrom"
},
"property": {
"type": "property",
"_type": "property",
"object": "@instance",
"property": "id"
}
@ -410,11 +428,11 @@
},
"run": [
{
"type": "setData",
"_type": "setData",
"object": "@instance",
"key": "tier",
"value": {
"type": "property",
"_type": "property",
"object": "@element",
"property": "id"
}

View file

@ -1,27 +1,59 @@
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 type { BaseLayer, GenericLayer } from "game/layers";
import { createLayer } from "game/layers";
import { persistent } from "game/persistence";
import { persistent, State } from "game/persistence";
import type { PlayerData } from "game/player";
import { render } from "util/vue";
import { computed, ref } from "vue";
import { computed, ref, watch } from "vue";
import Chat from "./Chat.vue";
import { processContentPacks } from "./contentPackLoader";
import core from "./contentPacks/core.json";
import { emit } from "./socket";
const knownContentPacks: Record<string, ContentPack> = {
core
};
/**
* @hidden
*/
export const main = createLayer("main", function (this: BaseLayer) {
const contentPacks = persistent<(ContentPack | string)[]>(["core"]);
const itemTypes = ref<Record<string, ItemType>>({});
const nodeTypes = ref<Record<string, NodeType>>({});
const customTypes = ref<Record<string, TypeType>>({});
const customObjects = ref<Record<string, Record<string, Record<string, State>>>>({});
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;
},
{ immediate: true }
);
const board = createBoard(() => ({
startNodes: () => [
{
type: "placeholder",
position: { x: 0, y: 0 }
}
],
startNodes: () => [],
types: {
placeholder: {
shape: Shape.Diamond,
@ -41,7 +73,7 @@ export const main = createLayer("main", function (this: BaseLayer) {
position.value = pos;
emit("set cursor position", pos);
}
}, 50)
}, 50);
return {
name: "Main",

View file

@ -11,6 +11,13 @@ import { useToast } from "vue-toastification";
import { ProxyState } from "util/proxies";
import satisfies from "semver/functions/satisfies";
import projInfo from "data/projInfo.json";
import {
ClientRoomData,
ClientToServerEvents,
GameState,
ServerToClientEvents
} from "alkatest-common/types";
import { main } from "./projEntry";
export const connected = ref<boolean>(false);
export const room = ref<string | null>(null);
@ -159,6 +166,9 @@ function setupSocket(socket: Socket<ServerToClientEvents, ClientToServerEvents>)
cursorPositions.value[id] = pos;
}
});
socket.on("set content packs", contentPacks => {
main.contentPacks.value = contentPacks;
});
}
function randomName(): string {

View file

@ -1,4 +1,5 @@
import { isArray } from "@vue/shared";
import type { ContentPack } from "alkatest-common/types";
import { globalBus } from "game/events";
import type { GenericLayer } from "game/layers";
import { addingLayers, persistentRefs } from "game/layers";
@ -39,6 +40,8 @@ export type State =
| number
| boolean
| DecimalSource
// TODO make it accept objects that only allow State types within them
| ContentPack
| { [key: string]: State }
| { [key: number]: State };