ai-prompt.ts 3.5 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556
  1. import { yamlPrompt } from "@json-render/yaml";
  2. import { buildEditUserPrompt } from "@json-render/core";
  3. import type { Spec } from "@json-render/core";
  4. import { stringify } from "yaml";
  5. import { catalog } from "./catalog";
  6. const GAME_RULES = [
  7. "Use numeric values only (no Math.PI, use 3.14159)",
  8. "For GamePlane as ground: rotation [-1.5708, 0, 0], scale [size, size, 1]",
  9. "Use descriptive keys for new objects (e.g. 'obj-tree-1-trunk', 'obj-tree-1-canopy')",
  10. "Object element keys start with 'obj-'",
  11. "The root element 'scene' is a Group whose children lists all top-level element keys",
  12. "When adding an object: add the element AND append the key to the scene children",
  13. "COMPOSE objects into recognizable structures. TREE RECIPE: trunk = GameCylinder with radiusTop 0.12-0.2, radiusBottom 0.15-0.25, height 3-6, color '#8B4513' or '#5C3317', position Y = height/2. Canopy = GameSphere with radius 1.5-3, color '#228B22' or '#2E8B57', position Y = trunk height + canopy radius * 0.7. The trunk must be TALL and THIN (radius << height). HOUSE: GameBox walls (width 3, height 2.5, depth 3) + GameCone roof on top. LAMP: GameCylinder radiusTop 0.05, radiusBottom 0.05, height 3 + GameSphere radius 0.2 with emissive material at top. NEVER scatter disconnected primitives randomly.",
  14. "SPATIAL COHERENCE: related parts share the same X/Z base. Stack vertically with exact Y math: if trunk height=4, trunk Y=2 (half height). Canopy radius=2, canopy Y = 4 + 2*0.7 = 5.4. Objects that belong together must visually touch or overlap.",
  15. "Be AMBITIOUS and THOROUGH: when the user asks for something, go all-in — build 10-20+ composed structures with varied sizes, not just a handful of loose shapes",
  16. "REPEATING DETAILS: when placing repeating elements (road dashes, fence posts, windows, floor tiles, pillars, streetlights along a road), space them evenly and use ENOUGH to cover the full length/area. A 100-unit road needs 20+ dashes, not 5. A building face 10 units wide needs 4-5 windows per floor, not 1. Do NOT be lazy with repetition — density sells the scene.",
  17. "Always set castShadow on lights and objects, receiveShadow on ground/floors, and use materials with varied colors, metalness, roughness for visual richness",
  18. "When building environments, include atmosphere (Sky, Fog, ambient light), ground plane with receiveShadow, and decorative elements — make it feel like a complete, coherent scene",
  19. ];
  20. function serializeSpec(spec: Spec): string {
  21. return stringify(spec, { indent: 2, lineWidth: 0 });
  22. }
  23. export function generateSystemPrompt(): string {
  24. return yamlPrompt(catalog, {
  25. system:
  26. "You are an expert 3D level designer AI for a json-render game engine. You build rich, detailed, immersive scenes using YAML patch operations. When the user describes a scene or asks for changes, go big — create dense, visually interesting environments with many objects, proper lighting, shadows, physics, varied materials, and atmospheric effects. Think like a AAA game level designer, not a minimal prototype builder.",
  27. mode: "standalone",
  28. editModes: ["merge"],
  29. customRules: GAME_RULES,
  30. });
  31. }
  32. export function generateUserPrompt(
  33. userPrompt: string,
  34. currentSpec: Spec,
  35. previousPrompts: string[] = [],
  36. ): string {
  37. const prevContext =
  38. previousPrompts.length > 0
  39. ? `Previous instructions:\n${previousPrompts.join("\n")}\n\n`
  40. : "";
  41. return (
  42. prevContext +
  43. buildEditUserPrompt({
  44. prompt: userPrompt,
  45. currentSpec,
  46. config: { modes: ["merge"] },
  47. format: "yaml",
  48. serializer: serializeSpec,
  49. })
  50. );
  51. }