754. Identidad de la lección
755. Nota de alcance
El diálogo con bloqueo del puntero es un tema documentado de CONTRABAND. Las ramas del intendente, la transferencia de suministros y la pista sobre una ruta que aparecen a continuación son ejemplos didácticos hipotéticos. No describen una implementación, un incidente ni una regla de juego documentados de CONTRABAND.
756. Objetivo de aprendizaje
Al finalizar esta lección, podrás especificar una rama narrativa como un contrato de elección trazable, separar la intención de la confirmación, asignar cada consecuencia persistente al sistema responsable y documentar una entrada, salida, observación y alternativa seguras para el bloqueo del puntero.
757. Por qué importa
Una elección de diálogo suele afectar a más de un sistema. Puede solicitar una transferencia de recursos, provocar una consecuencia en una relación o hacer avanzar un objetivo. Eso no convierte al controlador de diálogo en la autoridad sobre el inventario, las relaciones o las misiones.
Existe una escritura global oculta cuando un único controlador modifica silenciosamente estados que pertenecen a varios dominios. Es difícil validar una rama así porque la selección, la autorización, la persistencia y la presentación quedan reunidas en una sola operación. También puede producir un estado incoherente: el sistema de economía rechaza una transferencia mientras que los sistemas de relaciones o misiones ya han registrado el éxito.
Una rama trazable separa dos etapas:
- Intención: lo que el jugador solicitó.
- Resultado confirmado: lo que el sistema responsable validó y registró.
Solo el resultado confirmado debe autorizar las consecuencias posteriores que dependan del éxito.
El diálogo también crea un límite entre modos de interacción. El bloqueo del puntero depende de un ciclo de vida asíncrono del navegador y no queda garantizado por el mero hecho de llamar a requestPointerLock(). La restauración puede requerir una activación transitoria del usuario, y el éxito o el fallo deben observarse en lugar de darse por supuestos.
758. Conocimientos previos
Debes poder:
- distinguir el estado del diálogo de su presentación;
- identificar el sistema responsable de un hecho persistente;
- describir nodos y elecciones de diálogo;
- aplicar la distinción de la Etapa 2 entre presentación temporal y estado persistente del juego;
- reconocer el bloqueo del puntero como un modo de interacción y no como un hecho narrativo.
La lección anterior estableció que el panel de diálogo no debe convertirse en la autoridad sobre el estado duradero del mundo.
759. Concepto central: la intención no es una confirmación
Un contrato de elección útil identifica la elección, la solicitud, la autoridad correspondiente y los posibles resultados confirmados.
| Elemento del contrato | Pregunta | Ejemplo hipotético |
|---|---|---|
| Identidad de la elección | ¿Qué seleccionó el jugador? | offer_one_crate |
| Intención o solicitud | ¿Qué operación se solicita? | Transferir una caja del jugador |
| Sistema responsable | ¿Qué sistema valida y registra la operación? | Economía o inventario |
| Éxito confirmado | ¿Qué evento demuestra que terminó la operación? | SuppliesTransferCompleted |
| Rechazo confirmado | ¿Qué evento indica que no se completó? | SuppliesTransferRejected |
| Consumidores posteriores | ¿Qué sistemas pueden reaccionar al éxito confirmado? | Relaciones y misiones |
Un evento de intención no demuestra que la operación solicitada haya ocurrido. Los sistemas de relaciones y misiones no deben interpretar por su cuenta un evento SuppliesTransferRequested sin validar como si fuera un éxito. Si sus consecuencias requieren una transferencia completada, deben consumir SuppliesTransferCompleted.
760. Secuencia segura de eventos
La rama hipotética del intendente puede especificarse así:
1. El diálogo registra la selección:
choiceId = offer_one_crate
2. El diálogo emite una intención:
SuppliesTransferRequested
correlationId = transfer-42
quantity = 1
source = player
recipient = quartermaster
3. El sistema de economía procesa la solicitud:
- valida que la operación esté permitida
- valida que haya una caja disponible
- registra la retirada del inventario si es válida
4a. Si el registro se completa, economía emite:
SuppliesTransferCompleted
correlationId = transfer-42
quantity = 1
4b. Si se rechaza, economía emite:
SuppliesTransferRejected
correlationId = transfer-42
reasonCode = insufficient_supplies u otro motivo definido
5. Los sistemas de relaciones y misiones:
- no consideran la solicitud una prueba de éxito
- consumen el evento de finalización cuando corresponde
- no aplican consecuencias de éxito después de un rechazo
Los nombres son ejemplos, pero la distinción es obligatoria: la selección genera una solicitud; el sistema responsable genera el resultado confirmado.
761. Correlación, orden y ausencia de escrituras parciales
La especificación de una rama debe incluir algo más que nombres de eventos.
Correlación
Cada solicitud y su resultado necesitan un identificador de correlación compartido. Así, los consumidores pueden distinguir dos selecciones parecidas y asociar una finalización o un rechazo con la rama correcta.
Orden
Una consecuencia que dependa del éxito no debe procesarse antes del evento de finalización emitido por el sistema responsable. El orden lógico requerido es:
elección seleccionada
→ transferencia solicitada
→ economía valida y registra
→ resultado completado o rechazado
→ los consumidores dependientes del éxito reaccionan al resultado completado
En una implementación asíncrona, el orden de entrega puede variar. Por eso, los consumidores deben imponer la dependencia y no confiar solo en el momento de llegada.
Ausencia de escrituras parciales tras un rechazo
Si el sistema de economía rechaza la transferencia:
- el inventario no cambia;
- no se registra una mejora de relación asociada a una transferencia completada;
- no avanza ningún objetivo que requiera una transferencia completada;
- el jugador recibe una presentación de rechazo, no una presentación de éxito.
Entregas duplicadas y reintentos
Cada consumidor debe procesar el evento confirmado de forma idempotente mediante su identificador de evento o correlación. Recibir la misma confirmación más de una vez no debe aplicar repetidamente la misma consecuencia de relación o misión.
Fallo en otro dominio después de la confirmación
La transferencia confirmada demuestra que la operación económica se completó; no demuestra que todos los consumidores posteriores ya hayan persistido sus consecuencias. Cada consumidor registra su propio estado de procesamiento y puede reintentarlo de forma segura. Si el diseño exige que todos los dominios tengan éxito o fallen juntos, la especificación necesita un coordinador y una política de compensación explícitos, no la suposición de una transacción global invisible.
762. Mapa de responsabilidades
| Hecho | Sistema responsable | Desencadenante permitido |
|---|---|---|
| Caja retirada del inventario | Economía o inventario | Solicitud de transferencia validada |
| Transferencia completada o rechazada | Economía o inventario | Resultado del intento de validación y registro |
| Consecuencia de relación registrada | Sistema de relaciones | Finalización confirmada de la transferencia |
| Progreso del objetivo registrado | Sistema de misiones | Finalización confirmada, si el objetivo la requiere |
| Respuesta mostrada en el diálogo | Presentación del diálogo | Resultado confirmado de finalización o rechazo |
| Estado del bloqueo observado | Controlador del modo de interacción | Eventos del ciclo de vida del bloqueo |
La capa de diálogo puede mostrar un estado pendiente mientras se realiza la validación. No debe anunciar un éxito completo solo porque el jugador seleccionó la opción.
763. El bloqueo del puntero es un contrato de interacción asíncrono
La entrada y la salida del diálogo deben especificarse por separado de la persistencia narrativa.
Entrada
Cuando el juego con el puntero bloqueado entra en diálogo:
- Registra el modo de interacción anterior.
- Libera el bloqueo del puntero.
- Observa el cambio correspondiente en el ciclo de vida del bloqueo.
- Entrega el foco y la interpretación de la entrada a la interfaz de diálogo.
- No uses el estado del bloqueo como prueba del éxito o fracaso narrativo.
Salida
Cuando se cierra el diálogo:
- Registra la causa de salida, como un clic del jugador, una cancelación o un resultado narrativo asíncrono.
- Cierra la presentación y restaura la entrada de juego solo si el juego realmente va a continuar.
- Determina si el modo anterior requería el bloqueo del puntero.
- Comprueba que el lienzo siga siendo apto para recibirlo.
- Solicita el bloqueo solo desde una ruta compatible con los requisitos de activación del usuario del navegador.
- Observa
pointerlockchangepara confirmar el estado resultante. - Trata
pointerlockerror, o un estado que siga desbloqueado, como una solicitud fallida y no como un éxito.
requestPointerLock() es una solicitud, no una asignación de estado síncrona. El navegador puede exigir una activación transitoria del usuario. Si el diálogo solo se cierra después de un procesamiento asíncrono, es posible que la activación generada por el clic original ya no esté disponible.
Alternativa segura
Si la restauración automática no está disponible o se rechaza:
- mantén desactivado el control de cámara mediante movimiento mientras el puntero siga libre;
- presenta un control explícito como Haz clic para continuar o Haz clic para volver a capturar el puntero;
- llama a
requestPointerLock()desde esa nueva acción del jugador; - continúa observando los eventos del ciclo de vida;
- permite navegar sin bloqueo o cancelar cuando el diseño lo contemple.
No solicites el bloqueo repetidamente mediante un bucle oculto, no des por supuesto el éxito y no dejes activo el control de cámara mientras el puntero esté libre.
764. Caso hipotético combinado
Entrada al diálogo:
priorMode = pointer_locked_gameplay
liberar el bloqueo
esperar pointerlockchange
activar la entrada del diálogo
Selección de la opción:
emitir SuppliesTransferRequested(correlationId)
mostrar una respuesta pendiente
Resultado de economía:
si se registró:
emitir SuppliesTransferCompleted(correlationId)
de lo contrario:
emitir SuppliesTransferRejected(correlationId, reasonCode)
Respuesta posterior:
relaciones y misiones consumen solo el resultado completado
cada consumidor evita duplicados mediante el ID del evento o de correlación
el rechazo no produce escrituras persistentes que dependan del éxito
Salida del diálogo tras un resultado asíncrono:
restaurar el modo de entrada de juego
no suponer que el clic anterior aún aporta activación del usuario
solicitar el bloqueo solo si la acción de salida actual cumple los requisitos
confirmar mediante pointerlockchange
si ocurre pointerlockerror, mostrar un control explícito para continuar
Este diseño separa el resultado narrativo del resultado de interacción. El rechazo de una solicitud de bloqueo no revierte una transferencia de suministros confirmada, y una transferencia rechazada no determina si el navegador puede capturar el puntero.
765. Flujo de trabajo con IA
Usa la IA para inspeccionar un contrato, no para inventar sus autoridades ni sus reglas.
- Escribe la solicitud de la elección, el sistema responsable, los resultados confirmados y los consumidores.
- Añade un identificador de correlación y el orden requerido.
- Declara las reglas de rechazo y entrega duplicada.
- Especifica la entrada, la salida, los eventos del ciclo de vida, la activación del usuario y la alternativa del bloqueo del puntero.
- Pide a la IA que produzca un recorrido de eventos y señale consumidores que reaccionen a una intención como si fuera una confirmación.
- Pídele que identifique escrituras entre dominios, responsabilidades ausentes, rutas de rechazo incompletas y solicitudes de bloqueo cuyo éxito se dé por supuesto.
- Revisa tú el resultado antes de pedir una implementación.
Un prompt útil es:
«Revisa este contrato de elección narrativa y de interacción con bloqueo del puntero. Separa la intención del resultado confirmado, traza la correlación y el orden, identifica cada escritura persistente y su sistema responsable, comprueba el rechazo y las entregas duplicadas, y señala las transiciones de bloqueo que supongan activación del usuario o éxito síncrono. No inventes reglas del juego ni modifiques archivos».
766. Errores comunes
Distribuir una intención sin validar
onChoiceSelected():
emit SuppliesTransferRequested
relationship.onRequested(): rewardRelationship()
quest.onRequested(): advanceObjective()
economy.onRequested(): maybeRemoveCrate()
Esto permite que relaciones y misiones registren un éxito aunque economía rechace la transferencia.
Mutación global oculta
onChoiceSelected():
inventory.crates -= 1
relationship.quartermaster += 10
quest.suppliesObjective.complete = true
saveGame()
Este enfoque oculta la validación, la autoridad, el orden y el comportamiento de fallo dentro del código de diálogo.
Dar por restaurado el bloqueo
closeDialogue():
await finishDialogueWork()
canvas.requestPointerLock()
enableCameraLook()
Después del trabajo asíncrono, la activación transitoria del usuario puede haber caducado. La solicitud puede fallar y el control de cámara no debe activarse suponiendo que la captura tuvo éxito.
767. Práctica guiada
Diseña una rama hipotética en la que el jugador pueda compartir una pista sobre una ruta con un contacto o mantener la pista en privado. No atribuyas esta situación a CONTRABAND y no la implementes.
Completa la especificación:
| Campo | Tu especificación |
|---|---|
| IDs y texto mostrado de las elecciones | |
| Intención emitida por cada elección | |
| Identificador de correlación | |
| Sistema responsable de validar cada intención | |
| Eventos confirmados de éxito y rechazo | |
| Orden requerido de los eventos | |
| Consumidores del éxito confirmado | |
| Hecho persistente que corresponde a cada consumidor | |
| Regla para evitar escrituras parciales tras un rechazo | |
| Regla para entregas duplicadas | |
| Respuesta al jugador mientras está pendiente | |
| Respuesta tras la confirmación o el rechazo | |
| Modo anterior del bloqueo del puntero | |
| Causa de salida del diálogo | |
| Disponibilidad de activación del usuario al salir | |
| Eventos del ciclo de vida que deben observarse | |
| Alternativa tras el rechazo de la solicitud de bloqueo |
Después, comprueba:
- ¿Puede cada consumidor dependiente del éxito señalar un evento confirmado y no solo una intención?
- ¿La ruta de rechazo deja sin cambios todos los dominios que dependen del éxito?
- ¿Puede recibirse dos veces la finalización sin duplicar consecuencias?
- ¿Puede un revisor seguir cada hecho persistente hasta un único sistema responsable?
- ¿La restauración del bloqueo depende del estado observado y no solo de una llamada a una función?
- Si caducó la activación del usuario, ¿existe una alternativa basada en una nueva acción explícita?
- ¿Se tratan la finalización narrativa y la restauración del bloqueo como resultados independientes?
Revisa el contrato si alguna respuesta es negativa.
768. Evidencia de validación
La especificación completada debe incluir:
- al menos dos elecciones;
- una intención para cada elección;
- un sistema responsable de validar cada operación que pueda fallar;
- resultados confirmados de éxito y rechazo;
- datos de correlación compartidos;
- reglas explícitas de orden y ausencia de escrituras parciales;
- una regla de idempotencia o entrega duplicada;
- un sistema responsable de cada hecho persistente;
- condiciones de entrada y salida del bloqueo;
- las observaciones pertinentes del ciclo de vida;
- un análisis de la activación del usuario;
- una alternativa explícita tras el fallo de restauración.
La rama es trazable cuando un revisor puede determinar qué se solicitó, qué se confirmó, qué sistema persistió cada consecuencia y cómo el rechazo evita un éxito falso. La transición de interacción es segura cuando el juego no da por supuesta la captura del puntero y el jugador dispone de una acción clara para recuperarla.
769. Ideas clave
- Una elección seleccionada expresa una intención; no demuestra necesariamente que la operación se haya completado.
- El sistema responsable emite el resultado confirmado de éxito o rechazo.
- Los sistemas que dependen del éxito consumen la confirmación, no una intención sin validar.
- Las reglas de correlación, orden, rechazo e idempotencia evitan consecuencias incoherentes.
requestPointerLock()es asíncrono y puede requerir una activación transitoria del usuario.- El éxito del bloqueo debe observarse mediante su ciclo de vida y debe existir una alternativa explícita cuando falle la restauración.
- La persistencia narrativa y la restauración del modo de interacción son resultados independientes.
770. Próxima lección
Continúa con 2.9 — Sistemas de facciones. La consecuencia de relación confirmada que has identificado aquí se convertirá en una entrada para un contrato de relaciones bajo la autoridad del sistema de facciones. El diálogo seguirá siendo la fuente de la elección o del resultado confirmado, mientras que el sistema de relaciones de facción definirá y mantendrá la regla persistente.
771. Comprobación
Responde estas preguntas por tu cuenta antes de leer las respuestas.
El jugador selecciona «Ofrecer una caja». ¿Qué debe comunicar primero el sistema de diálogo si el sistema de economía tiene que validar la transferencia?
Mostrar respuesta y explicación
Respuesta: Una solicitud o intención de transferencia con datos de correlación
Por qué: La selección expresa una intención. El sistema de economía debe validar y registrar la transferencia antes de emitir una finalización o un rechazo confirmados.
¿Qué secuencia impide que los sistemas de relaciones y misiones registren un éxito después de una transferencia inválida?
Mostrar respuesta y explicación
Respuesta: El diálogo emite una solicitud correlacionada; economía valida y registra; los demás sistemas consumen solo la finalización confirmada
Por qué: Los consumidores que dependen del éxito necesitan el evento de finalización emitido por el sistema responsable. Una solicitud por sí sola no demuestra que la transferencia fuera válida ni que se registrara.
El sistema de economía rechaza una transferencia porque el objeto necesario no está disponible. ¿Qué reglas deben formar parte del contrato de la rama?
Mostrar respuesta y explicación
Respuesta: El inventario permanece sin cambios; No se producen escrituras de relación ni de misión que dependan del éxito; El diálogo presenta un rechazo asociado con el identificador de correlación original
Por qué: Un rechazo no debe dejar un estado parcial que dependa del éxito. La correlación permite asociar la respuesta de rechazo con la elección original.
¿Qué demuestra que una solicitud para restaurar el bloqueo del puntero tuvo éxito?
Mostrar respuesta y explicación
Respuesta: El estado observado del ciclo de vida confirma que el elemento previsto tiene el bloqueo
Por qué: requestPointerLock() es asíncrono. El código debe observar el ciclo de vida del bloqueo y gestionar los errores o un estado que siga desbloqueado.
El juego tenía el puntero bloqueado antes del diálogo. El jugador hace clic en una opción, pero el diálogo solo se cierra después de recibir un resultado asíncrono. La activación original del usuario ha caducado, requestPointerLock() se rechaza y el puntero sigue libre. ¿Qué transición de salida es la más segura?
Mostrar respuesta y explicación
Respuesta: Volver a un modo seguro sin bloqueo, mantener desactivado el control de cámara, mostrar un control explícito para continuar y solicitar el bloqueo desde esa nueva acción mientras se observan los eventos del ciclo de vida
Por qué: La transición segura no da por supuesta la captura, no deja activo el control de cámara mientras el puntero está libre y obtiene una nueva acción del usuario para repetir la solicitud. El fallo del bloqueo permanece separado del resultado narrativo confirmado.