Code frameworks
Reservas y modificadores
Comparte un presupuesto de puntos entre entradas, y deja que el contenido cambie números del personaje.
Reservas
Una reserva limita un grupo de entradas numéricas que un jugador rellena juntas, como tres rasgos que comparten un presupuesto de puntos. El servidor comprueba cada reserva cuando se crea o se edita un personaje.
"pools": {
"traits": {
"label": "Traits",
"inputs": ["might", "wit", "grace"],
"methods": {
"pointBuy": {
"budget": 6,
"min": 1,
"max": 5,
"costs": { "1": 0, "2": 1, "3": 2, "4": 4, "5": 6 }
}
}
}
}- Línea 23 Las entradas numéricas de la reserva. Cada una necesita una entrada en inputs.
- Línea 24 Los métodos que puede usar el jugador.
- Línea 26 El coste total de las puntuaciones no puede pasar del presupuesto.
- Línea 27 Cada puntuación es como mínimo esto.
- Línea 28 Cada puntuación es como máximo esto.
- Línea 29 El coste de cada puntuación. Cada puntuación de min a max necesita uno.
| Método | Qué comprueba |
|---|---|
| standardArray | Las puntuaciones son los valores enumerados, en cualquier orden. |
| pointBuy | Cada puntuación está entre min y max, y los costes suman como máximo el presupuesto. |
| manual | Cada puntuación está entre min y max. |
| roll | El servidor tira count puntuaciones de dice, suma el bonus opcional, multiplica por el multiplier opcional, y el jugador asigna los totales a las entradas. |
| budget | Cada puntuación está entre min y max, y las puntuaciones suman como máximo total. |
Una reserva debe declarar al menos un método. Una reserva con más de un método nombra en methodInput la entrada que elige el método. Los preajustes y los escenarios deben cumplir la reserva, y un escenario lo muestra:
{
"id": "new-keeper",
"framework": "keeper",
"parameters": {
"name": "Mira",
"max_hp": 10,
"might": 3,
"wit": 2,
"grace": 1
},
"expectedValues": {
"name": "Mira",
"max_hp": 10,
"hp": 10,
"might": 3,
"wit": 2,
"grace": 1
}
}- Líneas 7-9 Estas puntuaciones cuestan 2, 1 y 0, así que el total es 3 y el presupuesto es 6.
is not satisfied: pointBuy.budgetpointBuy costs has no entry for 5A pool declares at least one method.| Límite | Valor |
|---|---|
| Reservas por Framework | 4 |
| Entradas por reserva | 64 |
| Valores entre min y max | 64 |
| Presupuesto | 1000 |
El método budget toma total, min y max. Cada puntuación es un número entero de min a max, y las puntuaciones suman como máximo total. Las puntuaciones no tienen precio, así que sirve para un presupuesto de puntos de habilidad y no necesita costes.
Puntuaciones tiradas
El método roll hace que el servidor tire las puntuaciones, así que un jugador no puede escribir las suyas. Lleva dice y count, y dos claves opcionales, bonus y multiplier. dice es un grupo de dados, como "3d6" o "4d6dl1". count es cuántas puntuaciones tira el servidor y es igual al número de entradas del pool. bonus (de -20 a 100) se suma a cada total de dados y después multiplier (de 1 a 10) lo multiplica, de modo que cada puntuación es (total de dados + bonus) × multiplier. Una característica 3d6 × 5, como en La llamada de Cthulhu, es dice "3d6" con multiplier 5.
"pools": {
"traits": {
"label": "Traits",
"inputs": ["might", "wit", "grace"],
"methodInput": "method",
"methods": {
"manual": { "min": 15, "max": 90 },
"roll": { "dice": "3d6", "count": 3, "multiplier": 5 }
}
}
}- Línea 29 Una reserva con más de un método nombra la entrada que elige el método.
- Línea 31 El jugador puede escribir las puntuaciones.
- Línea 32 O el servidor tira 3 puntuaciones de 3d6, una para cada entrada.
methodInput nombra la entrada de texto que elige qué método usa un pool. La elección del jugador en esa entrada decide qué método comprueba el servidor, así que las mismas puntuaciones pueden someterse a la lista estándar, a la compra de puntos o a una tirada. Un pool con un solo método no necesita methodInput y siempre usa ese método. Con más de un método es obligatorio, y las options de la entrada deben ser exactamente los nombres de método del pool. Un escenario o preset fija la entrada a uno de los nombres. Un valor ausente o que no sea un nombre de método falla con method.missing o method.unknown.
"method": {
"label": "Scores by",
"options": ["manual", "roll"],
"defaultValue": "manual"
},- Línea 11 Exactamente los métodos de la reserva, nada más.
Todo total que la tirada pueda dar, tras el bonus y el multiplier, debe caber en min y max de cada entrada. 3d6 × 5 da de 15 a 90, así que cada rasgo permite de 15 a 90. Los dados son un solo grupo: hasta 10 dados de hasta 100 caras, y dl, dh, kl o kh descarta o conserva de 1 a 9 de ellos, conservando al menos un dado. Escribe un multiplicador con la clave multiplier, nunca dentro de los dados: 3d6x5 se rechaza.
The roll dice must be one group such asoutside the min/max of inputgive exactly one roll per input.Una clave que falta aparece como una lista JSON. La ruta de la lista nombra la clave, aquí pools, traits, methods, roll, dice, y el mensaje dice Required.
RequiredEn el formulario de creación de personaje, una reserva con roll muestra un botón Tirar puntuaciones en lugar de sus entradas, y Volver a tirar todo tras la primera tirada. El servidor tira y guarda un registro, así que el navegador nunca tira. Después el jugador asigna los totales a las entradas en cualquier orden. Tirar solo se ofrece en el formulario de creación.
Un escenario que elige el método roll aporta las tiradas él mismo en poolRolls, porque las tiradas reales solo existen en el servidor. poolRolls asocia el id de un pool con un slot por cada puntuación tirada. Un slot tiene un total, el número final tras el bonus y el multiplier, y opcionalmente dice y dropped, que son solo para leer. La comprobación del pool trata entonces los slots como el registro del servidor, así que las puntuaciones del escenario deben ser esos totales, en cualquier orden. Cada total debe ser uno que la tirada pueda producir. Sin poolRolls, un escenario que elige roll falla con roll.missing. Un preset no puede usar el método roll. El formulario de creación prueba la tirada real.
{
"id": "new-keeper",
"framework": "keeper",
"parameters": {
"name": "Mira",
"max_hp": 10,
"method": "roll",
"might": 60,
"wit": 45,
"grace": 75
},
"expectedValues": {
"name": "Mira",
"max_hp": 10,
"hp": 10,
"might": 60,
"wit": 45,
"grace": 75,
"method": "roll"
},
"poolRolls": {
"traits": [
{ "total": 45, "dice": [3, 4, 2] },
{ "total": 60, "dice": [5, 4, 3] },
{ "total": 75, "dice": [6, 5, 4] }
]
}
}- Línea 7 El escenario elige el método roll.
- Líneas 21-27 Las tiradas registradas. Cada total es la suma de los dados por el multiplier, aquí 9 × 5 = 45.
is not satisfied: roll.missingModificadores y efectos
Un modificador es un número que la plataforma deriva del contenido que tiene un personaje. Lo alimenta una lista que aplica efectos. Tus reglas lo leen como un campo. Este ejemplo deja que los amuletos puestos suban Guard.
El Framework del personaje declara el modificador.
"modifiers": {
"mod_guard": {
"label": "Guard",
"stack": "sum",
"each": { "min": -3, "max": 3 },
"total": { "min": -5, "max": 5 },
"appliesTo": ["guard"]
}
},- Línea 25 El nombre que ven los jugadores.
- Línea 26 sum suma todas las contribuciones. max toma la mayor.
- Línea 27 El rango de una contribución.
- Línea 28 El rango del total.
- Línea 29 El campo del ledger que alimenta el modificador. Es una etiqueta para las personas. Tus reglas hacen las cuentas.
La lista dice que sus entradas aplican efectos, y puede limitarlos.
"lists": {
"worn": {
"label": "Worn",
"of": "charm",
"max": 3,
"effects": true,
"distinctBy": "name",
"caps": [
{ "label": "Rings", "where": { "slot": { "eq": "Ring" } }, "max": 1 }
]
}
},- Línea 37 Las entradas de esta lista cambian modificadores.
- Línea 38 Una entrada con el mismo nombre que otra de la lista se rechaza con SAME_NAME.
- Líneas 39-41 Como máximo 1 entrada con la ranura Ring. Otra se rechaza con LIST_CAP.
El Framework de contenido nombra sus entradas de efecto. Deja fuera sus opciones y rangos, porque el compilador los rellena a partir de los modificadores del personaje.
{
"id": "charm",
"name": "Charm",
"elementType": "item",
"constructor": "rules/charm.ts",
"inputs": {
"name": { "label": "Name" },
"slot": {
"label": "Slot",
"options": ["Ring", "Cloak"],
"defaultValue": "Ring"
},
"effect_1": { "label": "Effect" },
"amount_1": { "label": "Amount" }
},
"effects": [{ "target": "effect_1", "amount": "amount_1" }],
"frames": [
{ "id": "charm-card", "source": "layouts/charm.json", "default": true }
],
"content": [
{
"id": "ring-of-guard",
"name": "Ring of Guard",
"parameters": {
"name": "Ring of Guard",
"slot": "Ring",
"effect_1": "Guard",
"amount_1": 2
}
},
{
"id": "ring-of-calm",
"name": "Ring of Calm",
"parameters": {
"name": "Ring of Calm",
"slot": "Ring",
"effect_1": "None",
"amount_1": 0
}
},
{
"id": "cloak-of-guard",
"name": "Cloak of Guard",
"parameters": {
"name": "Cloak of Guard",
"slot": "Cloak",
"effect_1": "Guard",
"amount_1": 1
}
},
{
"id": "cloak-spare",
"name": "Cloak of Guard",
"parameters": {
"name": "Cloak of Guard",
"slot": "Cloak",
"effect_1": "Guard",
"amount_1": 1
}
}
]
}- Líneas 13-14 El efecto y su cantidad no tienen opciones ni rango.
- Línea 16 Un efecto: qué entrada nombra el modificador y cuál guarda la cantidad.
- Línea 27 El preajuste da la etiqueta del modificador y la cantidad.
Una función del ledger lee el modificador como cualquier otro campo.
export function guard(currentValue: number, self: ElementRef): number {
return 10 + (element.var(self, "mod_guard") as number);
}- Línea 2 mod_guard se declara en el Framework, no lo devuelve el constructor.
El escenario demuestra cada caso. expectedValuesAfter comprueba el valor derivado después de un paso, y expectedModifiers comprueba los modificadores al final.
{
"id": "wear-charms",
"framework": "keeper",
"parameters": { "name": "Mira", "max_hp": 10 },
"expectedValues": { "name": "Mira", "max_hp": 10, "hp": 10, "guard": 10 },
"actions": [
{
"action": "wear",
"parameters": {},
"choose": { "charm": "ring-of-guard" },
"expectedWrites": [
{ "op": "list.add", "key": "worn", "value": "ring-of-guard" }
],
"expectedValuesAfter": { "guard": 12 }
},
{
"action": "wear",
"parameters": {},
"choose": { "charm": "ring-of-calm" },
"expectedRefusal": "LIST_CAP",
"expectedWrites": []
},
{
"action": "wear",
"parameters": {},
"choose": { "charm": "cloak-of-guard" },
"expectedWrites": [
{ "op": "list.add", "key": "worn", "value": "cloak-of-guard" }
],
"expectedValuesAfter": { "guard": 13 }
},
{
"action": "wear",
"parameters": {},
"choose": { "charm": "cloak-spare" },
"expectedRefusal": "SAME_NAME",
"expectedWrites": []
},
{
"action": "remove_charm",
"parameters": {},
"choose": { "charm": "ring-of-guard" },
"expectedWrites": [
{ "op": "list.remove", "key": "worn", "value": "ring-of-guard" }
],
"expectedValuesAfter": { "guard": 11 }
}
],
"expectedLists": { "worn": ["cloak-of-guard"] },
"expectedModifiers": { "mod_guard": 1 }
}- Línea 14 Guard 12: 10 más los 2 del anillo.
- Línea 20 Se rechaza un segundo anillo: el tope permite 1.
- Línea 30 Guard 13 con el 1 de la capa.
- Línea 36 Se rechaza una segunda capa con el mismo nombre.
- Línea 46 Guard 11 después de quitar el anillo.
- Línea 50 El total que queda es el 1 de la capa.
| Campo del modificador | Significado |
|---|---|
| label | El nombre que aparece en las opciones de efecto del contenido. |
| stack | sum o max. |
| each, total | El rango de una contribución y del total. |
| appliesTo | Hasta 8 nombres de los campos que alimenta el modificador. |
| appliesWhen | Una condición, como etiqueta. Tus reglas la aplican. |
Aún sin cubrir
Existen otras partes avanzadas que no se cubren aquí: listas de filas, elecciones y ranuras de contenido, subida de nivel y reconstrucciones, roles de combate y catálogos de registros. Cada una necesita su propia página.

