dcl-npc-toolkit-ai-version
Version:
A collection of tools for creating Non-Player-Characters (NPCs). These are capable of having conversations with the player, and play different animations. AI usage is added atop of it
194 lines (188 loc) • 6.61 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.generate = void 0;
const types_1 = require("../types");
const typeMaps = {
"string": "string",
"number": "float",
"boolean": "bool",
"int8": "sbyte",
"uint8": "byte",
"int16": "short",
"uint16": "ushort",
"int32": "int",
"uint32": "uint",
"int64": "long",
"uint64": "ulong",
"float32": "float",
"float64": "double",
};
/**
* C# Code Generator
*/
const capitalize = (s) => {
if (typeof s !== 'string')
return '';
return s.charAt(0).toUpperCase() + s.slice(1);
};
function generate(context, options) {
// enrich typeMaps with enums
context.enums.forEach((structure) => {
typeMaps[structure.name] = structure.name;
});
return [
...context.classes.map(structure => ({
name: `${structure.name}.cs`,
content: generateClass(structure, options.namespace)
})),
...context.interfaces.map(structure => ({
name: `${structure.name}.cs`,
content: generateInterface(structure, options.namespace),
})),
...context.enums.filter(structure => structure.name !== 'OPERATION').map((structure) => ({
name: `${structure.name}.cs`,
content: generateEnum(structure, options.namespace),
})),
];
}
exports.generate = generate;
function generateClass(klass, namespace) {
const indent = (namespace) ? "\t" : "";
return `${(0, types_1.getCommentHeader)()}
using Colyseus.Schema;
using Action = System.Action;
${namespace ? `\nnamespace ${namespace} {` : ""}
${indent}public partial class ${klass.name} : ${klass.extends} {
${klass.properties.map((prop) => generateProperty(prop, indent)).join("\n\n")}
${indent}\t/*
${indent}\t * Support for individual property change callbacks below...
${indent}\t */
${generateAllFieldCallbacks(klass, indent)}
${indent}}
${namespace ? "}" : ""}
`;
}
function generateEnum(_enum, namespace) {
const indent = namespace ? "\t" : "";
return `${(0, types_1.getCommentHeader)()}
${namespace ? `\nnamespace ${namespace} {` : ""}
${indent}public struct ${_enum.name} {
${_enum.properties
.map((prop) => {
let dataType = "int";
let value;
if (prop.type) {
if (isNaN(Number(prop.type))) {
value = `"${prop.type}"`;
dataType = "string";
}
else {
value = Number(prop.type);
dataType = Number.isInteger(value) ? 'int' : 'float';
}
}
else {
value = _enum.properties.indexOf(prop);
}
return `${indent}\tpublic const ${dataType} ${prop.name} = ${value};`;
})
.join("\n")}
${indent}}
${namespace ? "}" : ""}`;
}
function generateProperty(prop, indent = "") {
let typeArgs = `"${prop.type}"`;
let property = "public";
let langType;
let initializer = "";
if (prop.childType) {
const isUpcaseFirst = prop.childType.match(/^[A-Z]/);
langType = getType(prop);
typeArgs += `, typeof(${langType})`;
if (!isUpcaseFirst) {
typeArgs += `, "${prop.childType}"`;
}
initializer = `new ${langType}()`;
}
else {
langType = getType(prop);
initializer = `default(${langType})`;
}
property += ` ${langType} ${prop.name}`;
let ret = (prop.deprecated) ? `\t\t[System.Obsolete("field '${prop.name}' is deprecated.", true)]\n` : '';
return ret + `\t${indent}[Type(${prop.index}, ${typeArgs})]
\t${indent}${property} = ${initializer};`;
}
function generateInterface(struct, namespace) {
const indent = (namespace) ? "\t" : "";
return `${(0, types_1.getCommentHeader)()}
using Colyseus.Schema;
${namespace ? `\nnamespace ${namespace} {` : ""}
${indent}public class ${struct.name} {
${struct.properties.map(prop => `\t${indent}public ${getType(prop)} ${prop.name};`).join("\n")}
${indent}}
${namespace ? "}" : ""}
`;
}
function generateAllFieldCallbacks(klass, indent) {
//
// TODO: improve me. It would be great to generate less boilerplate in favor
// of a single implementation on C# Schema class itself.
//
const eventNames = [];
return `${klass.properties
.filter(prop => !prop.deprecated) // generate only for properties that haven't been deprecated.
.map(prop => {
const eventName = `__${prop.name}Change`;
eventNames.push(eventName);
const defaultNull = (prop.childType)
? "null"
: `default(${getType(prop)})`;
return `\t${indent}protected event PropertyChangeHandler<${getType(prop)}> ${eventName};
\t${indent}public Action On${capitalize(prop.name)}Change(PropertyChangeHandler<${getType(prop)}> __handler, bool __immediate = true) {
\t${indent}\tif (__callbacks == null) { __callbacks = new SchemaCallbacks(); }
\t${indent}\t__callbacks.AddPropertyCallback(nameof(this.${prop.name}));
\t${indent}\t${eventName} += __handler;
\t${indent}\tif (__immediate && this.${prop.name} != ${defaultNull}) { __handler(this.${prop.name}, ${defaultNull}); }
\t${indent}\treturn () => {
\t${indent}\t\t__callbacks.RemovePropertyCallback(nameof(${prop.name}));
\t${indent}\t\t${eventName} -= __handler;
\t${indent}\t};
\t${indent}}`;
}).join("\n\n")}
\t${indent}protected override void TriggerFieldChange(DataChange change) {
\t${indent}\tswitch (change.Field) {
${klass.properties.filter(prop => !prop.deprecated).map((prop, i) => {
return `\t${indent}\t\tcase nameof(${prop.name}): ${eventNames[i]}?.Invoke((${getType(prop)}) change.Value, (${getType(prop)}) change.PreviousValue); break;`;
}).join("\n")}
\t${indent}\t\tdefault: break;
\t\t${indent}}
\t${indent}}`;
}
function getChildType(prop) {
return typeMaps[prop.childType];
}
function getType(prop) {
if (prop.childType) {
const isUpcaseFirst = prop.childType.match(/^[A-Z]/);
let type;
if (prop.type === "ref") {
type = (isUpcaseFirst)
? prop.childType
: getChildType(prop);
}
else {
const containerClass = capitalize(prop.type);
type = (isUpcaseFirst)
? `${containerClass}Schema<${prop.childType}>`
: `${containerClass}Schema<${getChildType(prop)}>`;
}
return type;
}
else {
return (prop.type === "array")
? `${typeMaps[prop.childType] || prop.childType}[]`
: typeMaps[prop.type];
}
}
//# sourceMappingURL=csharp.js.map