He estat pensant en fer una plataforma web de RPG multijugador des dels meus dies com a desenvolupador de Flash. Hi ha moltes opcions de TTRPG en línia (moltes grans!) però cap d'elles es va sentir molt bé per a com m'agradaria jugar.
Has vistEl vídeoon Deborah Ann Woll mostra Jon Bernthal com jugar D&D? (hi ha 2M vistes!)
I si poguéssim fer alguna cosa que sembli així?
La idea
Una plataforma de RPG multijugador basada en la narració, lleugera en les regles, però alta en la narració compartida, amb resultats determinats pel paper d'un D20.
Sempre he estimatD&D mòdulsLes trobades detallades i els plans d'història èpica per als jugadors per descobrir com els seus personatges fan el seu camí a través d'una final climàtica.
I si pogués crear una manera perquè la gent faci els seus propis mòduls d'aventura, per a qualsevol gènere de RPG, i després deixi que els jugadors executin els seus personatges a través d'aquestes aventures?
Qui executaria els jocs, però? els mestres del joc són difícils de trobar. Què passa si pogués entrenar una IA per ser un mestre del joc? per executar realment un pla d'aventura ben estructurat (dissenyat per un humà!) que seria divertit per als jugadors i no només un munt deEl Slop?
Començar
He estat divertint-me construint moltes coses amb AI (veureEl meu projecte començaJo estaré utilitzant la meva pila preferida per ferD20Adventures.com, una aplicació web Next.js amb Tailwind per a la interfície d'usuari (need it even be said?) alimentat per laEl SDKutilitzant Gemini i aBase de dades ConvexS’ha desplaçat a Vercel.
I he decidit construir-lo en obert, publicant el codiEl GitHub.
El prototip
Per a un prototip, estic literalment començant des de l'escenari exposat en el podcast, un ranger que camina pel bosc a la nit sent un trencament a la distància, que resulta ser un ós.
El meu objectiu és construir una curta aventura d'un sol tret i veure si puc entrenar un DM d'IA per executar realment una petita sèrie de torns i trobades per a aquesta aventura.
Sinopsi: Una misteriosa crida d'un vell amic druida atrau un ranger reclus en el bosc de Valkarr.
The Midnight SummonsUna misteriosa crida d'un vell amic druida atrau un ranger reclus en el bosc de Valkarr.
Després de molts intents i errors, finalment vaig poder fer un joc complet i publicar-lo a YouTube:
Com funciona
Landing Page
La pàgina d'arribada és una imatge de gran heroi (generat a Midjourney amb una prompt de "El poder de la D20"). vaig afegir una simple fade en l'animació utilitzant la nova regla d'estil CSS @starting:
.fade-in {
@apply opacity-100 transition-opacity duration-1000 ease-in-out;
@starting-style {
opacity: 0;
}
}
Authentication
Per jugar al Quick Start, necessito un compte d'usuari. Això és per evitar cobrar molts diners a causa de les persones anònimes o robots que utilitzen les meves APIs.El clergatfa que sigui molt senzill afegir gestió d'usuaris, i els utilitzo en tots els meus projectes.
A més, tinc el seguiment de l'ús de token on limitaré l'ús amb un sistema de token, on comenceu amb prou tokens per fer un joc a través de la demostració, i després podeu comprar més a mesura que aneu.
The First Turn
Quan l'usuari aterra a la pàgina d'aventura per a l'inici ràpid, el primer que passa és que carregem les dades per a l'aventura de demostració, que és només un simple fitxer JSON (Com aquestUn mòdul d'aventura o pla en el meu sistema es compon d'una sèrie de trobades, que estan vinculades entre si amb instruccions per al LLM:
"encounters": [
{
"id": "broken-silence",
"title": "Broken Silence",
"intro": "Thalbern, a solitary ranger of the Valkarr woods, has always trusted the silence of the wilds more than the promises of men. Orphaned by border raiders and raised by the elves of the Valkrarr Forest, he has spent years living on the edge of Kordavos, guiding travelers, hunting for his own survival, and keeping his distance from the tangled politics of the city.\n\nYet on this night, a message delivered by a red squirrel bearing the unmistakable script of Wollandora, a trusted elven friend and druid, has drawn him from his hidden home. The note was simple and urgent: Meet me at the Old Standing Stones at midnight. The balance of the forest could depend on it.\n\nNow, as midnight approaches, Thalbern moves quietly through the dense undergrowth, guided by memory and instinct. It is dark with almost no moonlight coming through the forest canopy.\n\nSuddenly, the hush of the night is broken by a sharp crack. Something large has just stepped on a branch somewhere off in the distance.",
"instructions": "A perception check is appropriate if Thalbern investigates (low difficulty with a plus 3 modifier). If successful, he will determine it is a large creature that is approaching quickly. With a high roll (18+), he will determine it is an Owlbear. If combat ensues and Thalbern is below 25% health, Wollandora will intervene. If Thalbern avoids or defeats the Owlbear, or if Wollandora saves him, he proceeds to the Old Standing Stones.",
"image": "images/settings/realm-of-myr/the-midnight-summons/broken-silence-2.png",
"transitions": [
{
"condition": "If Thalbern successfully uses stealth to evade and proceeds cautiously towards the Standing Stones, go to meeting-at-stones.",
"encounter": "meeting-at-stones"
},
{
"condition": "If Thalbern fails a perception check, advance to owlbear-confrontation.",
"encounter": "owlbear-confrontation"
},
{
"condition": "If Thalbern fails any dice roll (including stealth, perception, or any other check), advance to owlbear-confrontation.",
"encounter": "owlbear-confrontation"
},
{
"condition": "If Thalbern does NOT successfully use stealth to evade, go to owlbear-confrontation.",
"encounter": "owlbear-confrontation"
},
{
"condition": "If Thalbern does nothing or takes no action, go to owlbear-confrontation.",
"encounter": "owlbear-confrontation"
},
{
"condition": "If Thalbern has a healthPercent of less than 50%, go to wollandora-intervention.",
"encounter": "wollandora-intervention"
}
]
},
{
"id": "owlbear-confrontation",
"title": "Owlbear Confrontation",
"intro": "From the direction of the sound, a little bit of eye shine glints in the shadows of the tree line. A hulking fifteen foot tall monster with the body of a giant bear and the head of an owl. As it crashes out from the undergrowth, it lets out a guttural squawk, clearly agitated and territorial.",
"instructions": "The Owlbear will attack. If Thalbern attempts an animal handling check (high difficulty) and succeeds, he can move past the Owlbear. If Thalbern wins initiative and attempts to hide, he can move past the Owlbear if he passes a medium difficulty stealth check. If Thalbern's health drops to a critical level, Wollandora appears and drives off the Owlbear, transitioning to 'wollandora-intervention'. If Thalbern defeats the Owlbear, describe his victory and transition to 'meeting-at-stones'.",
"image": "images/settings/realm-of-myr/the-midnight-summons/owlbear-confrontation.png",
"npc": [
{
"id": "owlbear",
"behavior": "Aggressively attacks any perceived threat. Will fight until heavily wounded or driven off.",
"initialInitiative": 1
}
],
"transitions": [
{
"condition": "Thalbern defeats the Owlbear, manages to evade it, successfully uses Animal Handling to pacify and move past it, or successfully rolls any other way to move past it.",
"encounter": "meeting-at-stones"
},
{
"condition": "Thalbern is reduced to critical health by the Owlbear.",
"encounter": "timely-rescue"
}
]
},
...
]
The First Reply
Atès que estem en mode de demostració, no hi ha cap aventura real creada encara en el fons. Això passa quan el jugador fa la primera resposta.formatNarrativeAction
Funció que utilitza la IA per avaluar la resposta per assegurar-se que és gramàticament correcta, en tercera persona i afegeix diàleg o altra prosa per fer que funcioni bé en un estil narratiu literari.
Després d'aplicar la formatació, la resposta s'envia a una acció del servidor. Com que es tracta d'una demostració, estem creant l'aventura a la base de dades quan es rep aquesta primera resposta.
Processing Player Responses
ElprocessTurnReply
La funció carrega el gir actual de Convex, les dades d'aventura de S3, i identifica la trobada específica i el personatge que realitza l'acció.
Amb aquest context, llavors utilitza la IA per determinar si l'acció és plausible (el meu fill en el moment de provar el joc va tenir el ranger llançant un nuke a l'óvul) i, si és així, si es requereix mecànicament un rodet de dits (per exemple, un "Roll d'atac" o un "Check Stealth"), incloent el tipus de rodet i la seva dificultat.
Podem fer-ho amb una trucada de funció, on podem especificar a l'IA que volem que es retornin dades estructurades, en aquest cas unarollRequirementSchema
:
import { z } from "zod";
export const rollRequirementSchema = z.union([
z.object({
rollType: z.string().describe("The type of roll required, e.g. 'Stealth Check'"),
difficulty: z.number().describe("The difficulty class (DC) for the roll"),
modifier: z.number().optional().describe("Bonus or penalty to the roll, e.g. +2 or -1"),
}),
z.null()
]);
export type RollRequirement = z.infer<typeof rollRequirementSchema>;
Després tenim una funció que envia un prompt detallat i l'esquema agenerateObject
:
export async function getRollRequirementForAction(action: string) {
const prompt = `
Given the following player or NPC action, determine if a dice roll is required for the character to attempt the action. If a roll is required, return a JSON object with "rollType" (choose the most appropriate from the list below) and "difficulty" (a number between 5 and 25). If no roll is required, return the JSON value null (not a string).
Possible roll types:
- Perception Check
- Investigation Check
- Insight Check
- Stealth Check
...
Examples:
Action: "Try to sneak past the guards."
Result: { "rollType": "Stealth Check", "difficulty": 15 }
Action: "Attack the goblin."
Result: { "rollType": "Attack Roll", "difficulty": 12 }
Action: "Try to determine what the sound is."
Result: { "rollType": "Perception Check", "difficulty": 10 }
Action: "Say hello."
Result: null
Now, given the following action, determine the roll requirement.
Action: "${action}"
`;
try {
const result = await generateObject({
schema: rollRequirementSchema,
prompt,
});
if (
result.object &&
typeof result.object === "object" &&
"rollType" in result.object &&
(result.object.rollType === "null" || result.object.rollType === "none" || result.object.rollType === "")
) {
return null;
}
return result.object ?? null;
} catch (error) {
throw error;
}
}
Després d'afegir la resposta del jugador a la història de la volta, si es requereix un rotllo de dígits, actualitzem l'estat del personatge en les dades de la rotació convexa per reflectir que han respost però el seu torn no està complet.
Una vegada que l’usuari es desplaça, tenim unresolvePlayerRollResult
Acció del servidor que actualitza la narració amb una visualització del resultat i escriu prosa que descriu el resultat.També tenim una altra trucada de la funció d'IA per actualitzar la salut i l'estat de tots els personatges al seu torn.
NPC Actions
Cada trobada inclou informació sobre la motivació dels NPC i quan es combina amb el context de la narració de l'aventura fins ara, esperem que l'IA pugui generar una bona resposta, després generar el seu propi rellotge i actualització de resultats.
Training AI to Run RPGs
Si alguna vegada heu intentat fer una sessió de joc amb IA en un xat, sabeu com de ràpid pot sortir de les pistes.
Esperem que proporcionant el context adequat, instruccions concretes i trucades de funcions de dades estructurades, puguem obtenir una experiència que sigui agradable.
Per exemple, quan em pregunto per què la IA no va seguir les instruccions de trobada en una de les meves proves, això és el que el xat de Cursor tenia a dir-me:
En resum, el LLM no va seguir les seves instruccions explícites per a la transició quan un personatge fracassa una comprovació de la percepció, tot i que es va proporcionar una evidència clara d'aquest fracàs i una regla de transició per a aquest escenari específic.
En resum, el LLM no va seguir les seves instruccions explícites per a la transició quan un personatge fracassa una comprovació de la percepció, tot i que es va proporcionar una evidència clara d'aquest fracàs i una regla de transició per a aquest escenari específic.
Suposo que la casualitat de l'IA no fer exactament el que s'espera podria ser part de la diversió.
Aquí teniu un exemple deUna de les sessions de jocs.
Podeu veure el codi font complet d'aquest projecte agithub.com/Joanpolacek/d20adventures.com