{"version":3,"file":"entity.luau","names":[],"sources":["..\\..\\..\\src\\centurion\\types\\entity.ts"],"sourcesContent":["import { store } from \"@lisachandra/core/store\";\nimport { includes } from \"@lisachandra/core/utils/string\";\nimport { is } from \"@lisachandra/core/utils/type\";\nimport type { ComponentKey } from \"@lisachandra/matter\";\nimport { Components } from \"@lisachandra/matter\";\n/**\n * Defines and registers custom Centurion types for entities. These types are used for command\n * argument parsing and validation.\n */\nimport type { SingleArgumentType } from \"@rbxts/centurion\";\nimport { ListTypeBuilder, TransformResult, TypeBuilder } from \"@rbxts/centurion\";\nimport type { AnyEntity } from \"@rbxts/matter\";\nimport { isEmpty } from \"@rbxts/object-utils\";\nimport RegExp from \"@rbxts/regexp\";\nimport { Players } from \"@rbxts/services\";\n\nconst prefixRegexp = RegExp(\"^@([^()]+)(?:\\\\((.*)\\\\))?$\");\n\nfunction getEntitySuggestionsFunction<T>(\n\tincludeEntities = false,\n\tincludePlayers = false,\n\tincludePrefix = true,\n): NonNullable<SingleArgumentType<T>[\"suggestions\"]> {\n\treturn (_text: string, _executor: Player) => {\n\t\tconst entities: Array<string> = [];\n\t\tif (includeEntities) {\n\t\t\tfor (const [entityId] of store.world) {\n\t\t\t\tentities.push(tostring(entityId));\n\t\t\t}\n\t\t}\n\n\t\tconst suggestions: Array<string> = [...entities];\n\t\tif (includePrefix) {\n\t\t\tsuggestions.unshift(\"@me\");\n\t\t}\n\n\t\tif (includePlayers) {\n\t\t\tconst playerNames = Players.GetPlayers().map((player) => player.Name);\n\t\t\tfor (const playerName of playerNames) {\n\t\t\t\tsuggestions.push(playerName);\n\t\t\t}\n\t\t}\n\n\t\treturn suggestions;\n\t};\n}\n\nfunction parsePrefix(prefix: string, args: string, executor: Player): N<Array<AnyEntity>> {\n\tconst executorEntityId = executor.GetAttribute<AnyEntity>(\"serverEntityId\")!;\n\tconst accumulator: Array<AnyEntity> = [];\n\n\tswitch (prefix) {\n\t\tcase \"all\":\n\t\tcase \"others\": {\n\t\t\tfor (const [entityId] of store.world) {\n\t\t\t\tif (prefix === \"others\" && entityId === executorEntityId) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\taccumulator.push(entityId);\n\t\t\t}\n\n\t\t\tbreak;\n\t\t}\n\t\tcase \"except\":\n\t\tcase \"only\": {\n\t\t\tconst input = args.split(\";\");\n\t\t\tconst result = Entities.transform(input, executor);\n\n\t\t\tif (!typeIs(result?.value, \"table\")) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tfor (const entityId of result.value) {\n\t\t\t\taccumulator.push(entityId);\n\t\t\t}\n\n\t\t\tbreak;\n\t\t}\n\t\tcase \"me\": {\n\t\t\taccumulator.push(executorEntityId);\n\n\t\t\tbreak;\n\t\t}\n\t\tcase \"query\": {\n\t\t\tconst components = args\n\t\t\t\t.split(\";\")\n\t\t\t\t.map((key) => {\n\t\t\t\t\tif (!is<ComponentKey>(key) || !Components[key]) {\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\n\t\t\t\t\treturn Components[key];\n\t\t\t\t})\n\t\t\t\t.filterUndefined();\n\n\t\t\tif (isEmpty(components)) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tfor (const [entityId] of store.world.query(...components)) {\n\t\t\t\taccumulator.push(entityId);\n\t\t\t}\n\n\t\t\tbreak;\n\t\t}\n\t\tdefault: {\n\t\t\treturn;\n\t\t}\n\t}\n\n\treturn accumulator;\n}\n\n/** Base entity type. Transforms text input into an entity ID, verifying its existence in the world. */\nexport const Entity = TypeBuilder.create<AnyEntity>(\"entity\")\n\t.transform((text, executor) => {\n\t\tif (text === \"@me\") {\n\t\t\t// eslint-disable-next-line no-param-reassign -- allowed\n\t\t\ttext = executor.Name;\n\t\t}\n\n\t\t// Attempt to get the entity ID from the player's attribute or parse it as a number.\n\t\tconst entityId = (Players.FindFirstChild(text)?.GetAttribute<AnyEntity>(\"serverEntityId\") ??\n\t\t\ttonumber(text)) as N<AnyEntity>;\n\n\t\t// Check if the entity ID is a number and exists in the world.\n\t\treturn typeIs(entityId, \"number\") && store.world.contains(entityId)\n\t\t\t? TransformResult.ok(entityId)\n\t\t\t: TransformResult.err(`${text} Entity not found.`);\n\t})\n\t.suggestions(getEntitySuggestionsFunction(true, true, true))\n\t.markForRegistration()\n\t.build();\n\n/**\n * Registered Centurion list type for entity IDs, supporting prefix syntax (`@me`, `@all`,\n * `@others`, `@query`, `@except`, `@only`).\n *\n * @remarks\n *   Prefixes are resolved via `parsePrefix`; bare entity IDs fall through to the single `Entity`\n *   transform.\n */\nexport const Entities = ListTypeBuilder.create<Array<AnyEntity>>(\"entities\")\n\t.transform((input, executor) => {\n\t\tlet entities: Array<AnyEntity> = [];\n\n\t\tfor (const text of input) {\n\t\t\tif (includes(text, \"@\")) {\n\t\t\t\tconst match = prefixRegexp.exec(text);\n\t\t\t\tconst prefix = match?.[1];\n\t\t\t\tconst args = match?.[2] ?? \"\";\n\t\t\t\tconst parsedEntities =\n\t\t\t\t\tprefix !== undefined ? parsePrefix(prefix, args, executor) : undefined;\n\n\t\t\t\tif (!parsedEntities) {\n\t\t\t\t\treturn TransformResult.err(`Prefix '${text}' is not valid.`);\n\t\t\t\t}\n\n\t\t\t\tif (prefix === \"except\") {\n\t\t\t\t\tentities = entities.filter((entity) => !parsedEntities.includes(entity));\n\t\t\t\t} else if (prefix === \"only\") {\n\t\t\t\t\tentities = entities.filter((entity) => parsedEntities.includes(entity));\n\t\t\t\t} else {\n\t\t\t\t\tfor (const parsedEntity of parsedEntities) {\n\t\t\t\t\t\tentities.push(parsedEntity);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst result = Entity.transform(text, executor);\n\t\t\tif (typeIs(result?.value, \"number\")) {\n\t\t\t\tentities.push(result.value);\n\t\t\t}\n\t\t}\n\n\t\t// Filter duplicate entries\n\t\tentities = entities.filter((entityId, index) => entities.indexOf(entityId) === index);\n\n\t\treturn TransformResult.ok(entities);\n\t})\n\t.suggestions((_text, executor) => {\n\t\tconst suggestions = getEntitySuggestionsFunction(true, true, true)(\"\", executor);\n\t\tsuggestions.unshift(\"@all\", \"@others\", \"@query()\", \"@except()\", \"@only()\");\n\n\t\treturn suggestions;\n\t})\n\t.markForRegistration()\n\t.build();\n"],"mappings":";;AAAA;AACA;AACA;AAEA;;;;;;;AAMA;AAAA;AAAA;AAAA;AAEA;AACA;AACA;AAEA;AAEA;;;;;;;;;;AAKC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAsBD;;AAEA;AACC;AACA;AAEA;;;;;;AAGE;AACC;AACC;AACD;AAEA;AACD;AAEA;;;;;;AAIA;AACA;AAEA;AAAA;;AAEA;AAFA;AACC;AACD;AAEA;AACC;AACD;AAEA;;;AAGA;AAEA;;;AAGA;AAAA;AAAA;AAAA;;;;;;AAAA;;AASmB;AATnB;AAAA;AAAA;AAAA;;;;AASmB;AATnB;AAAA;AAAA;;;;;;AASmB;AATnB;AAAA;AAWA;AACC;AACD;AAEA;AACC;AACD;AAEA;;AAEQ;AACR;AACD;;AAGD;AACD;;AAGA;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA4BA","ignoreList":[]}