""" unity_tools.py ============== Complete tool registry for the Unity Agent — a code-generation assistant that can scaffold a GTA 6-level open-world game inside Unity 2022+. Every tool in this module is a self-contained Python class that, when its ``run()`` method is invoked, emits production-quality, compilable C# Unity scripts as a string. The agent orchestrator loops over the requested tools, collects the generated source, and writes the files into a Unity project scaffold (see :class:`SetupUnityProjectTool` and :class:`GenerateCompleteGameTool`). The registry covers every major gameplay system required by a modern open world title: * Locomotion & cameras (player, FPS, third-person) * Vehicles & driving physics * Procedural world generation (city, terrain, roads, buildings, water) * Combat (weapons, health, ragdolls, explosions) * AI (NPCs, pedestrians, traffic, police/wanted) * Systems (economy, quests, missions, factions, skills, crafting, loot) * Presentation (UI, minimap, radio, phone, cutscenes, photo mode) * Persistence (save system, inventory, garages, properties) * Atmosphere (day/night, weather, particles, audio, CCTV) * Networking (multiplayer netcode) All generated C# targets Unity 2022.3 LTS APIs and uses idiomatic patterns: ``[SerializeField]`` private fields, ``[Header]`` grouping, ``Awake``/``Start``/ ``Update``/``FixedUpdate``/``LateUpdate`` lifecycle hooks, ``Rigidbody`` physics, ``NavMeshAgent`` navigation, the legacy ``Input`` class with optional new Input System hooks, ``Canvas``-based UI, ``AudioSource`` 3D audio, and ``ParticleSystem`` effects. The module exposes :data:`TOOL_REGISTRY` (a list of instantiated tools) and the convenience function :func:`get_tool` for lookup by name. """ from __future__ import annotations import json import os import textwrap from typing import Any, Dict, List, Optional # --------------------------------------------------------------------------- # Base infrastructure # --------------------------------------------------------------------------- class BaseUnityTool: """Base class for every Unity code-generation tool. Subclasses set the class attributes ``name``, ``description`` and ``input_schema`` and override :meth:`run` to return generated C# source code. The ``run`` method receives validated keyword arguments matching ``input_schema`` and must return a string (typically the contents of one or more ``.cs`` files joined by separators). """ name: str = "base_tool" description: str = "Base Unity tool — override me." input_schema: Dict[str, Any] = {} def run(self, **kwargs: Any) -> str: """Generate and return C# source code for this tool.""" raise NotImplementedError def get_schema(self) -> Dict[str, Any]: """Return the JSON-schema-style descriptor for this tool.""" return { "name": self.name, "description": self.description, "input_schema": self.input_schema, } # -- helpers ----------------------------------------------------------- @staticmethod def _wrap(code: str) -> str: """Dedent a triple-quoted code block for clean emission.""" return textwrap.dedent(code).strip() + "\n" TOOL_REGISTRY: List[BaseUnityTool] = [] def register_tool(cls: type) -> type: """Class decorator that instantiates a tool and adds it to the registry.""" TOOL_REGISTRY.append(cls()) return cls def get_tool(name: str) -> Optional[BaseUnityTool]: """Look up a registered tool by its ``name`` attribute.""" for tool in TOOL_REGISTRY: if tool.name == name: return tool return None def list_tools() -> List[Dict[str, Any]]: """Return schema descriptors for every registered tool.""" return [tool.get_schema() for tool in TOOL_REGISTRY] # =========================================================================== # 1. Player controller # =========================================================================== @register_tool class CreatePlayerControllerTool(BaseUnityTool): """Generate a complete third-person player controller. Produces a ``PlayerController.cs`` MonoBehaviour implementing WASD movement relative to the camera, sprint, crouch, jump, melee attack, and a camera-aware facing direction. Uses a ``CharacterController`` for collision, gravity integration in ``Update``, and root-motion-free animation hooks (``IsWalking``, ``IsSprinting``, ``IsGrounded``, ``Speed``) for an Animator. """ name = "create_player_controller" description = ( "Generate a full third-person player controller with WASD, sprint, " "crouch, jump, melee and animation hooks." ) input_schema = { "type": "object", "properties": { "class_name": {"type": "string", "default": "PlayerController"}, "move_speed": {"type": "number", "default": 5.0}, "sprint_multiplier": {"type": "number", "default": 2.0}, "crouch_multiplier": {"type": "number", "default": 0.5}, "jump_height": {"type": "number", "default": 2.0}, "gravity": {"type": "number", "default": -19.62}, "rotation_speed": {"type": "number", "default": 10.0}, }, } def run( self, class_name: str = "PlayerController", move_speed: float = 5.0, sprint_multiplier: float = 2.0, crouch_multiplier: float = 0.5, jump_height: float = 2.0, gravity: float = -19.62, rotation_speed: float = 10.0, **_: Any, ) -> str: return self._wrap(f''' using System; using UnityEngine; namespace OpenWorld.Player {{ /// /// Third-person character controller. Movement is camera-relative and /// supports walking, sprinting, crouching, jumping and a simple melee /// attack. Animation parameters (IsWalking, IsSprinting, IsGrounded, /// Speed, Attack) are pushed every frame for an Animator to consume. /// [RequireComponent(typeof(CharacterController))] [RequireComponent(typeof(Animator))] public class {class_name} : MonoBehaviour {{ [Header("Movement")] [SerializeField] private float moveSpeed = {move_speed}f; [SerializeField] private float sprintMultiplier = {sprint_multiplier}f; [SerializeField] private float crouchMultiplier = {crouch_multiplier}f; [SerializeField] private float rotationSpeed = {rotation_speed}f; [SerializeField] private float jumpHeight = {jump_height}f; [SerializeField] private float gravity = {gravity}f; [Header("Ground Check")] [SerializeField] private Transform groundCheck; [SerializeField] private float groundDistance = 0.4f; [SerializeField] private LayerMask groundMask; [Header("Camera")] [SerializeField] private Transform cameraTransform; [Header("Combat")] [SerializeField] private float meleeRange = 2.0f; [SerializeField] private float meleeDamage = 25.0f; [SerializeField] private float meleeCooldown = 0.8f; private CharacterController _controller; private Animator _animator; private Vector3 _velocity; private bool _isGrounded; private bool _isCrouching; private float _meleeTimer; private float _turnSmoothVelocity; private static readonly int IsWalkingHash = Animator.StringToHash("IsWalking"); private static readonly int IsSprintingHash = Animator.StringToHash("IsSprinting"); private static readonly int IsGroundedHash = Animator.StringToHash("IsGrounded"); private static readonly int SpeedHash = Animator.StringToHash("Speed"); private static readonly int AttackHash = Animator.StringToHash("Attack"); private void Awake() {{ _controller = GetComponent(); _animator = GetComponent(); if (cameraTransform == null && Camera.main != null) cameraTransform = Camera.main.transform; }} private void Update() {{ GroundCheck(); HandleMovement(); HandleJump(); HandleCrouch(); HandleMelee(); ApplyGravity(); UpdateAnimator(); }} private void GroundCheck() {{ if (groundCheck != null) _isGrounded = Physics.CheckSphere(groundCheck.position, groundDistance, groundMask); else _isGrounded = _controller.isGrounded; }} private void HandleMovement() {{ float horizontal = Input.GetAxis("Horizontal"); float vertical = Input.GetAxis("Vertical"); Vector3 direction = new Vector3(horizontal, 0f, vertical).normalized; float targetSpeed = moveSpeed; if (_isCrouching) targetSpeed *= crouchMultiplier; else if (Input.GetKey(KeyCode.LeftShift)) targetSpeed *= sprintMultiplier; if (direction.magnitude >= 0.1f) {{ float targetAngle = Mathf.Atan2(direction.x, direction.z) * Mathf.Rad2Deg; if (cameraTransform != null) targetAngle += cameraTransform.eulerAngles.y; float angle = Mathf.SmoothDampAngle( transform.eulerAngles.y, targetAngle, ref _turnSmoothVelocity, 1f / rotationSpeed); transform.rotation = Quaternion.Euler(0f, angle, 0f); Vector3 moveDir = Quaternion.Euler(0f, targetAngle, 0f) * Vector3.forward; _controller.Move(moveDir.normalized * targetSpeed * Time.deltaTime); }} }} private void HandleJump() {{ if (Input.GetButtonDown("Jump") && _isGrounded && !_isCrouching) _velocity.y = Mathf.Sqrt(jumpHeight * -2f * gravity); }} private void HandleCrouch() {{ if (Input.GetKeyDown(KeyCode.C) && _isGrounded) {{ _isCrouching = !_isCrouching; _controller.height = _isCrouching ? 1.2f : 2.0f; _controller.center = new Vector3(0f, _controller.height / 2f, 0f); }} }} private void HandleMelee() {{ _meleeTimer -= Time.deltaTime; if (Input.GetMouseButtonDown(0) && _meleeTimer <= 0f) {{ _meleeTimer = meleeCooldown; _animator.SetTrigger(AttackHash); if (Physics.Raycast(transform.position + Vector3.up, transform.forward, out RaycastHit hit, meleeRange)) {{ var dmg = hit.collider.GetComponent(); dmg?.TakeDamage(meleeDamage, transform.position); }} }} }} private void ApplyGravity() {{ if (_isGrounded && _velocity.y < 0f) _velocity.y = -2f; _velocity.y += gravity * Time.deltaTime; _controller.Move(_velocity * Time.deltaTime); }} private void UpdateAnimator() {{ float horizontal = Input.GetAxis("Horizontal"); float vertical = Input.GetAxis("Vertical"); float speed = new Vector2(horizontal, vertical).sqrMagnitude; _animator.SetBool(IsWalkingHash, speed > 0.01f); _animator.SetBool(IsSprintingHash, Input.GetKey(KeyCode.LeftShift) && speed > 0.01f); _animator.SetBool(IsGroundedHash, _isGrounded); _animator.SetFloat(SpeedHash, speed); }} private void OnDrawGizmosSelected() {{ if (groundCheck != null) {{ Gizmos.color = Color.red; Gizmos.DrawWireSphere(groundCheck.position, groundDistance); }} }} }} }} ''') # =========================================================================== # 2. Vehicle controller # =========================================================================== @register_tool class CreateVehicleControllerTool(BaseUnityTool): """Generate a vehicle controller with arcade-realistic car physics.""" name = "create_vehicle_controller" description = ( "Generate a full car controller with acceleration/steering curves, " "brakes, handbrake, gears, engine sound, lights and damage." ) input_schema = { "type": "object", "properties": { "class_name": {"type": "string", "default": "VehicleController"}, "max_torque": {"type": "number", "default": 1500.0}, "max_brake": {"type": "number", "default": 3000.0}, "max_steer": {"type": "number", "default": 30.0}, }, } def run( self, class_name: str = "VehicleController", max_torque: float = 1500.0, max_brake: float = 3000.0, max_steer: float = 30.0, **_: Any, ) -> str: return self._wrap(f''' using System; using System.Collections.Generic; using UnityEngine; namespace OpenWorld.Vehicles {{ /// /// Arcade-realistic four-wheel car controller. WheelColliders provide /// traction; a single Rigidbody carries mass and inertia. Includes gear /// ratios, engine audio pitch, head/tail/brake/reverse lights and a /// simple impact-damage model. /// [RequireComponent(typeof(Rigidbody))] public class {class_name} : MonoBehaviour {{ [Header("Wheel Colliders")] [SerializeField] private WheelCollider frontLeft; [SerializeField] private WheelCollider frontRight; [SerializeField] private WheelCollider rearLeft; [SerializeField] private WheelCollider rearRight; [Header("Wheel Meshes")] [SerializeField] private Transform frontLeftMesh; [SerializeField] private Transform frontRightMesh; [SerializeField] private Transform rearLeftMesh; [SerializeField] private Transform rearRightMesh; [Header("Engine")] [SerializeField] private float maxMotorTorque = {max_torque}f; [SerializeField] private float maxBrakeTorque = {max_brake}f; [SerializeField] private float maxSteerAngle = {max_steer}f; [SerializeField] private float[] gearRatios = {{ 3.6f, 2.5f, 1.8f, 1.3f, 1.0f, 0.8f }}; [SerializeField] private float reverseRatio = -3.2f; [SerializeField] private float downforce = 50f; [Header("Audio")] [SerializeField] private AudioSource engineAudio; [SerializeField] private float minPitch = 0.4f; [SerializeField] private float maxPitch = 2.2f; [Header("Lights")] [SerializeField] private Light[] headLights; [SerializeField] private Light[] brakeLights; [SerializeField] private Light[] reverseLights; [SerializeField] private Material brakeMat; [SerializeField] private Color brakeOn = Color.red; [SerializeField] private Color brakeOff = new Color(0.3f, 0f, 0f); [Header("Damage")] [SerializeField] private float maxHealth = 100f; [SerializeField] private ParticleSystem smokePrefab; [SerializeField] private ParticleSystem firePrefab; private Rigidbody _rb; private int _currentGear; private float _currentSpeed; private float _health; private bool _braking; private bool _reversing; public float SpeedKph => _currentSpeed * 3.6f; public float HealthPercent => Mathf.Clamp01(_health / maxHealth); public int CurrentGear => _reversing ? -1 : _currentGear + 1; private void Awake() {{ _rb = GetComponent(); _health = maxHealth; if (engineAudio != null) engineAudio.loop = true; }} private void FixedUpdate() {{ Steer(); Drive(); Handbrake(); UpdateWheels(); ApplyDownforce(); AutoShift(); }} private void Update() {{ UpdateLights(); UpdateAudio(); }} private void Steer() {{ float steerInput = Input.GetAxis("Horizontal"); float speedFactor = Mathf.Clamp01(_currentSpeed / 30f); float steer = maxSteerAngle * steerInput * (1f - speedFactor * 0.4f); frontLeft.steerAngle = steer; frontRight.steerAngle = steer; }} private void Drive() {{ float throttle = Input.GetAxis("Vertical"); _braking = Mathf.Abs(throttle) < 0.05f; _reversing = throttle < -0.05f && _currentSpeed < 2f; float ratio = _reversing ? reverseRatio : gearRatios[_currentGear]; float torque = throttle * maxMotorTorque * Mathf.Abs(ratio); foreach (var wc in new[] {{ rearLeft, rearRight }}) {{ wc.motorTorque = _braking ? 0f : torque; wc.brakeTorque = _braking ? maxBrakeTorque * 0.1f : 0f; }} foreach (var wc in new[] {{ frontLeft, frontRight }}) wc.brakeTorque = _braking ? maxBrakeTorque * 0.1f : 0f; _currentSpeed = _rb.velocity.magnitude; }} private void Handbrake() {{ if (Input.GetKey(KeyCode.Space)) {{ rearLeft.brakeTorque = maxBrakeTorque; rearRight.brakeTorque = maxBrakeTorque; rearLeft.motorTorque = 0f; rearRight.motorTorque = 0f; }} }} private void UpdateWheels() {{ UpdateWheelPose(frontLeft, frontLeftMesh); UpdateWheelPose(frontRight, frontRightMesh); UpdateWheelPose(rearLeft, rearLeftMesh); UpdateWheelPose(rearRight, rearRightMesh); }} private static void UpdateWheelPose(WheelCollider collider, Transform mesh) {{ if (mesh == null) return; collider.GetWorldPose(out Vector3 pos, out Quaternion rot); mesh.SetPositionAndRotation(pos, rot); }} private void ApplyDownforce() {{ _rb.AddForce(-transform.up * downforce * _rb.velocity.magnitude); }} private void AutoShift() {{ float speed = SpeedKph; int target = Mathf.Clamp(Mathf.FloorToInt(speed / 30f), 0, gearRatios.Length - 1); if (target != _currentGear) _currentGear = target; }} private void UpdateLights() {{ bool brakeOnNow = _braking || Input.GetKey(KeyCode.Space); if (brakeMat != null) brakeMat.SetColor("_EmissionColor", brakeOnNow ? brakeOn : brakeOff); foreach (var l in brakeLights) if (l != null) l.enabled = brakeOnNow; foreach (var l in reverseLights) if (l != null) l.enabled = _reversing; }} private void UpdateAudio() {{ if (engineAudio == null) return; float t = Mathf.Clamp01(_currentSpeed / 60f); float gearFactor = (_currentGear + 1f) / gearRatios.Length; engineAudio.pitch = Mathf.Lerp(minPitch, maxPitch, t * gearFactor + 0.2f); }} private void OnCollisionEnter(Collision collision) {{ float impact = collision.relativeVelocity.magnitude; if (impact > 8f) {{ _health -= impact * 0.5f; if (_health < maxHealth * 0.3f && smokePrefab != null && !smokePrefab.isPlaying) smokePrefab.Play(); if (_health <= 0f && firePrefab != null) firePrefab.Play(); }} }} }} }} ''') # =========================================================================== # 3. Procedural city # =========================================================================== @register_tool class CreateProceduralCityTool(BaseUnityTool): """Generate a procedural grid-city builder with multiple districts.""" name = "create_procedural_city" description = ( "Generate a procedural grid city with 200+ buildings, districts " "(downtown, residential, industrial), variable heights and window " "patterns per district." ) input_schema = { "type": "object", "properties": { "class_name": {"type": "string", "default": "ProceduralCity"}, "grid_size": {"type": "integer", "default": 16}, "block_size": {"type": "number", "default": 40.0}, "road_width": {"type": "number", "default": 12.0}, }, } def run( self, class_name: str = "ProceduralCity", grid_size: int = 16, block_size: float = 40.0, road_width: float = 12.0, **_: Any, ) -> str: return self._wrap(f''' using System; using System.Collections.Generic; using UnityEngine; namespace OpenWorld.World {{ /// /// Procedurally generates a grid-based city. The grid is partitioned /// into districts by region; each district defines building height /// ranges, footprint sizes and window texture tints. Roads separate /// every block. /// public class {class_name} : MonoBehaviour {{ [Header("Grid")] [SerializeField] private int gridSize = {grid_size}; [SerializeField] private float blockSize = {block_size}f; [SerializeField] private float roadWidth = {road_width}f; [SerializeField] private int seed = 1337; [Header("Prefabs")] [SerializeField] private GameObject roadPrefab; [SerializeField] private GameObject sidewalkPrefab; [SerializeField] private Material[] windowMaterials; [Header("Districts")] [SerializeField] private District[] districts; private System.Random _rng; [Serializable] public struct District {{ public string name; public Vector2 heightRange; public Vector2 footprintRange; public Color windowTint; public float density; }} private void Awake() {{ _rng = new System.Random(seed); Generate(); }} public void Generate() {{ ClearChildren(); for (int x = 0; x < gridSize; x++) for (int z = 0; z < gridSize; z++) {{ Vector3 center = new Vector3( x * (blockSize + roadWidth), 0f, z * (blockSize + roadWidth)); District d = PickDistrict(x, z); BuildBlock(center, d); BuildRoads(center); }} }} private District PickDistrict(int x, int z) {{ // Downtown near centre, residential mid, industrial edges. float dx = (x - gridSize / 2f) / gridSize; float dz = (z - gridSize / 2f) / gridSize; float dist = Mathf.Sqrt(dx * dx + dz * dz); int idx = dist < 0.25f ? 0 : dist < 0.45f ? 1 : 2; return districts[Mathf.Clamp(idx, 0, districts.Length - 1)]; }} private void BuildBlock(Vector3 center, District d) {{ int count = Mathf.FloorToInt(blockSize / 12f * d.density); for (int i = 0; i < count; i++) {{ float fx = (float)_rng.NextDouble(); float fz = (float)_rng.NextDouble(); Vector3 pos = center + new Vector3( (fx - 0.5f) * (blockSize - 6f), 0f, (fz - 0.5f) * (blockSize - 6f)); float height = Mathf.Lerp(d.heightRange.x, d.heightRange.y, (float)_rng.NextDouble()); float footprint = Mathf.Lerp(d.footprintRange.x, d.footprintRange.y, (float)_rng.NextDouble()); GameObject b = GameObject.CreatePrimitive(PrimitiveType.Cube); b.transform.SetParent(transform); b.transform.position = pos + Vector3.up * (height * 0.5f); b.transform.localScale = new Vector3(footprint, height, footprint); ApplyWindowMaterial(b, d, height); }} }} private void ApplyWindowMaterial(GameObject building, District d, float height) {{ Renderer r = building.GetComponent(); if (r == null || windowMaterials == null || windowMaterials.Length == 0) return; Material m = windowMaterials[_rng.Next(windowMaterials.Length)]; Material inst = new Material(m); inst.SetColor("_EmissionColor", d.windowTint * 0.5f); inst.mainTextureScale = new Vector2( Mathf.Max(1, Mathf.FloorToInt(height / 3f)), Mathf.Max(1, Mathf.FloorToInt(height / 3f))); r.sharedMaterial = inst; }} private void BuildRoads(Vector3 center) {{ if (roadPrefab == null) return; Vector3 horiz = center + Vector3.left * (blockSize + roadWidth) * 0.5f; Vector3 vert = center + Vector3.back * (blockSize + roadWidth) * 0.5f; Instantiate(roadPrefab, horiz, Quaternion.Euler(0f, 90f, 0f), transform) .transform.localScale = new Vector3(blockSize, 1f, roadWidth); Instantiate(roadPrefab, vert, Quaternion.identity, transform) .transform.localScale = new Vector3(roadWidth, 1f, blockSize); }} private void ClearChildren() {{ for (int i = transform.childCount - 1; i >= 0; i--) DestroyImmediate(transform.GetChild(i).gameObject); }} }} }} ''') # =========================================================================== # 4. Third-person camera # =========================================================================== @register_tool class CreateThirdPersonCameraTool(BaseUnityTool): """Generate a smooth follow third-person camera with collision & zoom.""" name = "create_third_person_camera" description = ( "Generate a third-person camera with smooth follow, collision, " "zoom and aim mode." ) input_schema = { "type": "object", "properties": { "class_name": {"type": "string", "default": "ThirdPersonCamera"}, "distance": {"type": "number", "default": 6.0}, "height": {"type": "number", "default": 2.5}, "sensitivity": {"type": "number", "default": 3.0}, }, } def run( self, class_name: str = "ThirdPersonCamera", distance: float = 6.0, height: float = 2.5, sensitivity: float = 3.0, **_: Any, ) -> str: return self._wrap(f''' using UnityEngine; namespace OpenWorld.Camera {{ /// /// Smooth third-person follow camera with mouse orbit, scroll-wheel /// zoom, camera collision via SphereCast and an aim mode that pulls /// the camera over the shoulder. /// public class {class_name} : MonoBehaviour {{ [Header("Target")] [SerializeField] private Transform target; [SerializeField] private Vector3 targetOffset = new Vector3(0f, {height}f, 0f); [Header("Orbit")] [SerializeField] private float distance = {distance}f; [SerializeField] private float minDistance = 2f; [SerializeField] private float maxDistance = 12f; [SerializeField] private float sensitivity = {sensitivity}f; [SerializeField] private float pitchMin = -40f; [SerializeField] private float pitchMax = 80f; [SerializeField] private float smoothTime = 0.12f; [Header("Collision")] [SerializeField] private float collisionRadius = 0.3f; [SerializeField] private LayerMask collisionMask = ~0; [Header("Aim Mode")] [SerializeField] private Vector3 aimOffset = new Vector3(0.7f, 1.4f, 0f); [SerializeField] private float aimDistance = 2.5f; private float _yaw; private float _pitch = 15f; private float _currentDistance; private Vector3 _smoothVel; private void Start() {{ _currentDistance = distance; if (target != null) _yaw = target.eulerAngles.y; Cursor.lockState = CursorLockMode.Locked; }} private void LateUpdate() {{ if (target == null) return; ReadInput(); UpdateOrbit(); HandleCollision(); transform.position = Vector3.SmoothDamp( transform.position, DesiredPosition(), ref _smoothVel, smoothTime); transform.LookAt(target.position + targetOffset); }} private void ReadInput() {{ _yaw += Input.GetAxis("Mouse X") * sensitivity; _pitch -= Input.GetAxis("Mouse Y") * sensitivity; _pitch = Mathf.Clamp(_pitch, pitchMin, pitchMax); float scroll = Input.GetAxis("Mouse ScrollWheel"); distance = Mathf.Clamp(distance - scroll * 4f, minDistance, maxDistance); }} private void UpdateOrbit() {{ if (Input.GetMouseButton(1)) {{ target.rotation = Quaternion.Euler(0f, _yaw, 0f); }} }} private Vector3 DesiredPosition() {{ bool aim = Input.GetMouseButton(1); float dist = aim ? aimDistance : _currentDistance; Vector3 offset = aim ? aimOffset : targetOffset; Quaternion rot = Quaternion.Euler(_pitch, _yaw, 0f); return target.position + offset - (rot * Vector3.forward * dist); }} private void HandleCollision() {{ Vector3 origin = target.position + targetOffset; Vector3 dir = transform.position - origin; float dist = dir.magnitude; dir.Normalize(); if (Physics.SphereCast(origin, collisionRadius, dir, out RaycastHit hit, dist, collisionMask)) _currentDistance = Mathf.Min(distance, hit.distance); else _currentDistance = Mathf.Lerp(_currentDistance, distance, 0.1f); }} }} }} ''') # =========================================================================== # 5. FPS controller # =========================================================================== @register_tool class CreateFpsControllerTool(BaseUnityTool): """Generate an FPS controller with mouse-look, lean and crouch.""" name = "create_fps_controller" description = ( "Generate an FPS controller with mouse look, WASD, jump, crouch " "and lean mechanics." ) input_schema = { "type": "object", "properties": { "class_name": {"type": "string", "default": "FpsController"}, "move_speed": {"type": "number", "default": 5.0}, "look_sensitivity": {"type": "number", "default": 2.0}, }, } def run( self, class_name: str = "FpsController", move_speed: float = 5.0, look_sensitivity: float = 2.0, **_: Any, ) -> str: return self._wrap(f''' using UnityEngine; namespace OpenWorld.Player {{ /// /// First-person controller. Mouse look drives a yaw on the body and /// pitch on the camera. WASD moves on the horizontal plane, jump uses /// an impulse, crouch shrinks the capsule, and Q/E lean the camera. /// [RequireComponent(typeof(CharacterController))] public class {class_name} : MonoBehaviour {{ [Header("Movement")] [SerializeField] private float walkSpeed = {move_speed}f; [SerializeField] private float sprintSpeed = 8f; [SerializeField] private float crouchSpeed = 2.5f; [SerializeField] private float jumpHeight = 1.4f; [SerializeField] private float gravity = -19.62f; [Header("Look")] [SerializeField] private Transform cameraTransform; [SerializeField] private float lookSensitivity = {look_sensitivity}f; [SerializeField] private float pitchLimit = 85f; [Header("Lean")] [SerializeField] private float leanAngle = 25f; [SerializeField] private float leanSpeed = 8f; [SerializeField] private float leanOffset = 0.6f; [Header("Crouch")] [SerializeField] private float standHeight = 2f; [SerializeField] private float crouchHeight = 1.2f; private CharacterController _cc; private float _pitch; private float _targetLean; private float _currentLean; private Vector3 _velocity; private bool _isCrouching; private void Awake() {{ _cc = GetComponent(); if (cameraTransform == null && Camera.main != null) cameraTransform = Camera.main.transform; Cursor.lockState = CursorLockMode.Locked; }} private void Update() {{ Look(); Move(); Crouch(); Lean(); Jump(); ApplyGravity(); }} private void Look() {{ float mx = Input.GetAxis("Mouse X") * lookSensitivity; float my = Input.GetAxis("Mouse Y") * lookSensitivity; transform.Rotate(Vector3.up * mx); _pitch = Mathf.Clamp(_pitch - my, -pitchLimit, pitchLimit); cameraTransform.localEulerAngles = new Vector3(_pitch, 0f, _currentLean); }} private void Move() {{ float h = Input.GetAxis("Horizontal"); float v = Input.GetAxis("Vertical"); Vector3 move = (transform.right * h + transform.forward * v); float speed = _isCrouching ? crouchSpeed : (Input.GetKey(KeyCode.LeftShift) ? sprintSpeed : walkSpeed); _cc.Move(move.normalized * speed * Time.deltaTime); }} private void Crouch() {{ if (Input.GetKeyDown(KeyCode.LeftControl)) {{ _isCrouching = !_isCrouching; _cc.height = _isCrouching ? crouchHeight : standHeight; _cc.center = new Vector3(0f, _cc.height * 0.5f, 0f); }} }} private void Lean() {{ if (Input.GetKey(KeyCode.Q)) _targetLean = leanAngle; else if (Input.GetKey(KeyCode.E)) _targetLean = -leanAngle; else _targetLean = 0f; _currentLean = Mathf.Lerp(_currentLean, _targetLean, leanSpeed * Time.deltaTime); Vector3 camPos = cameraTransform.localPosition; camPos.x = Mathf.Lerp(camPos.x, (_targetLean / leanAngle) * leanOffset, leanSpeed * Time.deltaTime); cameraTransform.localPosition = camPos; }} private void Jump() {{ if (Input.GetButtonDown("Jump") && _cc.isGrounded) _velocity.y = Mathf.Sqrt(jumpHeight * -2f * gravity); }} private void ApplyGravity() {{ if (_cc.isGrounded && _velocity.y < 0f) _velocity.y = -2f; _velocity.y += gravity * Time.deltaTime; _cc.Move(_velocity * Time.deltaTime); }} }} }} ''') # =========================================================================== # 6. Day/night cycle # =========================================================================== @register_tool class CreateDayNightCycleTool(BaseUnityTool): """Generate a 24-hour day/night cycle with sky gradients and lighting.""" name = "create_day_night_cycle" description = ( "Generate a 24-hour sun arc, sky-color gradient, automatic street " "lights and crowd-density changes." ) input_schema = { "type": "object", "properties": { "class_name": {"type": "string", "default": "DayNightCycle"}, "day_length_minutes": {"type": "number", "default": 24.0}, }, } def run( self, class_name: str = "DayNightCycle", day_length_minutes: float = 24.0, **_: Any, ) -> str: return self._wrap(f''' using UnityEngine; namespace OpenWorld.World {{ /// /// Drives a continuous 24-hour cycle. The directional sun rotates around /// the world; the ambient/sky color is sampled from a gradient; street /// lights toggle automatically at dusk/dawn; a callback notifies other /// systems (e.g. crowd density) of the current hour. /// public class {class_name} : MonoBehaviour {{ [Header("Time")] [SerializeField] private float dayLengthMinutes = {day_length_minutes}f; [SerializeField] [Range(0f, 24f)] private float startHour = 8f; public float CurrentHour {{ get; private set; }} [Header("Sun")] [SerializeField] private Light sun; [SerializeField] private float sunIntensityDay = 1.2f; [SerializeField] private float sunIntensityNight = 0.05f; [SerializeField] private Color sunColorDay = new Color(1f, 0.96f, 0.86f); [SerializeField] private Color sunColorSunset = new Color(1f, 0.55f, 0.25f); [Header("Sky")] [SerializeField] private Gradient skyColor; [SerializeField] private Gradient ambientColor; [SerializeField] private Material skyboxMaterial; [Header("Lights")] [SerializeField] private Light[] streetLights; [SerializeField] private float lightOnHour = 18.5f; [SerializeField] private float lightOffHour = 6.5f; public event System.Action OnHourChanged; private float _hoursPerSecond; private void Awake() {{ _hoursPerSecond = 24f / (dayLengthMinutes * 60f); CurrentHour = startHour; if (skyColor == null) BuildDefaultGradient(); }} private void Update() {{ CurrentHour += _hoursPerSecond * Time.deltaTime; if (CurrentHour >= 24f) CurrentHour -= 24f; UpdateSun(); UpdateSky(); UpdateStreetLights(); OnHourChanged?.Invoke(CurrentHour); }} private void UpdateSun() {{ float sunAngle = (CurrentHour / 24f) * 360f - 90f; if (sun != null) {{ sun.transform.rotation = Quaternion.Euler(sunAngle, 170f, 0f); float dayFactor = Mathf.Clamp01(Mathf.Sin((CurrentHour / 24f) * Mathf.PI * 2f - Mathf.PI / 2f) * 0.5f + 0.5f); sun.intensity = Mathf.Lerp(sunIntensityNight, sunIntensityDay, dayFactor); sun.color = (CurrentHour < 7f || CurrentHour > 17f) ? sunColorSunset : sunColorDay; }} }} private void UpdateSky() {{ float t = CurrentHour / 24f; Color sky = skyColor.Evaluate(t); Color ambient = ambientColor.Evaluate(t); RenderSettings.skybox?.SetColor("_Tint", sky); RenderSettings.ambientLight = ambient; if (skyboxMaterial != null) skyboxMaterial.SetColor("_Tint", sky); }} private void UpdateStreetLights() {{ bool on = CurrentHour >= lightOnHour || CurrentHour <= lightOffHour; if (streetLights == null) return; foreach (var l in streetLights) if (l != null) l.enabled = on; }} private void BuildDefaultGradient() {{ skyColor = new Gradient(); var keys = new GradientColorKey[] {{ new GradientColorKey(new Color(0.02f, 0.03f, 0.08f), 0.00f), // midnight new GradientColorKey(new Color(0.5f, 0.3f, 0.4f), 0.22f), // dawn new GradientColorKey(new Color(0.53f, 0.81f, 0.98f), 0.35f), // morning new GradientColorKey(new Color(0.53f, 0.81f, 0.98f), 0.65f), // afternoon new GradientColorKey(new Color(1.0f, 0.45f, 0.20f), 0.78f), // sunset new GradientColorKey(new Color(0.02f, 0.03f, 0.08f), 1.00f), // night }}; var alpha = new GradientAlphaKey[] {{ new GradientAlphaKey(1f, 0f), new GradientAlphaKey(1f, 1f) }}; skyColor.SetKeys(keys, alpha); ambientColor = new Gradient(); var akeys = new GradientColorKey[] {{ new GradientColorKey(new Color(0.05f, 0.05f, 0.1f), 0f), new GradientColorKey(new Color(0.4f, 0.4f, 0.5f), 0.3f), new GradientColorKey(new Color(0.6f, 0.6f, 0.6f), 0.5f), new GradientColorKey(new Color(0.4f, 0.35f, 0.3f), 0.78f), new GradientColorKey(new Color(0.05f, 0.05f, 0.1f), 1f), }}; ambientColor.SetKeys(akeys, alpha); }} }} }} ''') # =========================================================================== # 7. AI NPC # =========================================================================== @register_tool class CreateAiNpcTool(BaseUnityTool): """Generate an AI NPC with NavMesh wandering, fleeing and chasing.""" name = "create_ai_npc" description = ( "Generate an AI NPC with NavMesh wandering, fleeing, chasing, " "ragdoll death and voice lines." ) input_schema = { "type": "object", "properties": { "class_name": {"type": "string", "default": "AiNpc"}, "wander_radius": {"type": "number", "default": 30.0}, "detect_range": {"type": "number", "default": 15.0}, }, } def run( self, class_name: str = "AiNpc", wander_radius: float = 30.0, detect_range: float = 15.0, **_: Any, ) -> str: return self._wrap(f''' using System; using UnityEngine; using UnityEngine.AI; namespace OpenWorld.AI {{ /// /// Generic NPC driven by a NavMeshAgent. Behaviour switches between /// Wander, Flee and Chase states based on a threat Transform. On death /// the agent is disabled and a ragdoll is enabled. Periodic voice lines /// are played from an AudioClip pool. /// [RequireComponent(typeof(NavMeshAgent))] public class {class_name} : MonoBehaviour {{ public enum State {{ Wander, Flee, Chase, Dead }} [Header("Movement")] [SerializeField] private float wanderRadius = {wander_radius}f; [SerializeField] private float detectRange = {detect_range}f; [SerializeField] private float fleeRange = 25f; [SerializeField] private float chaseSpeed = 5.5f; [SerializeField] private float wanderSpeed = 1.6f; [SerializeField] private float repathInterval = 3f; [Header("Health")] [SerializeField] private float maxHealth = 100f; [Header("Audio")] [SerializeField] private AudioClip[] voiceLines; [SerializeField] private float voiceInterval = 12f; [SerializeField] private AudioSource audioSource; [Header("Ragdoll")] [SerializeField] private Rigidbody[] ragdollBodies; [SerializeField] private Collider[] ragdollColliders; [SerializeField] private Animator animator; [Header("Threat")] [SerializeField] private Transform player; private NavMeshAgent _agent; private State _state = State.Wander; private float _health; private float _repathTimer; private float _voiceTimer; public float Health => _health; public State CurrentState => _state; private void Awake() {{ _agent = GetComponent(); _health = maxHealth; SetRagdoll(false); }} private void Update() {{ switch (_state) {{ case State.Wander: Wander(); break; case State.Flee: Flee(); break; case State.Chase: Chase(); break; case State.Dead: return; }} UpdateVoice(); }} private void Wander() {{ _agent.speed = wanderSpeed; _repathTimer -= Time.deltaTime; if (_repathTimer <= 0f || _agent.remainingDistance < 0.5f) {{ _repathTimer = repathInterval; _agent.SetDestination(RandomNavPoint(transform.position, wanderRadius)); }} if (animator != null) animator.SetFloat("Speed", _agent.velocity.magnitude); if (ThreatDistance() < detectRange && IsThreatHostile()) _state = State.Chase; }} private void Flee() {{ _agent.speed = chaseSpeed; if (player == null) {{ _state = State.Wander; return; }} Vector3 away = (transform.position - player.position).normalized * fleeRange; _agent.SetDestination(transform.position + away); if (ThreatDistance() > fleeRange) _state = State.Wander; }} private void Chase() {{ if (player == null) {{ _state = State.Wander; return; }} _agent.speed = chaseSpeed; _agent.SetDestination(player.position); if (ThreatDistance() > detectRange * 1.5f) _state = State.Wander; }} private float ThreatDistance() => player == null ? Mathf.Infinity : Vector3.Distance(transform.position, player.position); protected virtual bool IsThreatHostile() => true; private static Vector3 RandomNavPoint(Vector3 origin, float radius) {{ for (int i = 0; i < 20; i++) {{ Vector3 rnd = origin + UnityEngine.Random.insideUnitSphere * radius; rnd.y = origin.y; if (NavMesh.SamplePosition(rnd, out NavMeshHit hit, radius, NavMesh.AllAreas)) return hit.position; }} return origin; }} private void UpdateVoice() {{ if (audioSource == null || voiceLines == null || voiceLines.Length == 0) return; _voiceTimer -= Time.deltaTime; if (_voiceTimer <= 0f) {{ _voiceTimer = voiceInterval; audioSource.PlayOneShot(voiceLines[UnityEngine.Random.Range(0, voiceLines.Length)]); }} }} public void TakeDamage(float dmg, Vector3 from) {{ if (_state == State.Dead) return; _health -= dmg; if (_health <= 0f) Die(); else _state = State.Flee; }} private void Die() {{ _state = State.Dead; _agent.isStopped = true; _agent.enabled = false; if (animator != null) animator.enabled = false; SetRagdoll(true); foreach (var rb in ragdollBodies) if (rb != null) rb.AddForce((transform.forward + Vector3.up) * 3f, ForceMode.Impulse); }} private void SetRagdoll(bool enabled) {{ foreach (var rb in ragdollBodies) if (rb != null) rb.isKinematic = !enabled; foreach (var col in ragdollColliders) if (col != null) col.enabled = enabled; }} }} }} ''') # =========================================================================== # 8. Pickup system # =========================================================================== @register_tool class CreatePickupSystemTool(BaseUnityTool): """Generate a pickup system for weapons, health, money and ammo.""" name = "create_pickup_system" description = ( "Generate pickups for weapons, health, money and ammo with glow " "and rotation." ) input_schema = { "type": "object", "properties": { "class_name": {"type": "string", "default": "Pickup"}, }, } def run(self, class_name: str = "Pickup", **_: Any) -> str: return self._wrap(f''' using UnityEngine; namespace OpenWorld.Items {{ /// /// Generic pickup. Rotates and bobs in place, emits a glow, and on /// trigger applies an effect (health, money, ammo or weapon) to the /// player via the IPickupReceiver interface. /// public class {class_name} : MonoBehaviour {{ public enum PickupType {{ Health, Money, Ammo, Weapon, Armor }} [Header("Config")] [SerializeField] private PickupType type = PickupType.Health; [SerializeField] private int amount = 25; [SerializeField] private string weaponId = "pistol"; [SerializeField] private bool respawn = true; [SerializeField] private float respawnDelay = 30f; [Header("Visual")] [SerializeField] private float rotateSpeed = 90f; [SerializeField] private float bobHeight = 0.2f; [SerializeField] private float bobSpeed = 2f; [SerializeField] private Light glowLight; [SerializeField] private ParticleSystem glowParticles; [SerializeField] private AudioClip collectSound; private Vector3 _startPos; private AudioSource _audio; private void Awake() {{ _startPos = transform.position; _audio = GetComponent(); if (glowLight != null) {{ glowLight.color = type switch {{ PickupType.Health => Color.green, PickupType.Money => Color.yellow, PickupType.Ammo => Color.cyan, PickupType.Weapon => Color.red, PickupType.Armor => Color.blue, _ => Color.white, }}; }} }} private void Update() {{ transform.Rotate(Vector3.up, rotateSpeed * Time.deltaTime); float y = Mathf.Sin(Time.time * bobSpeed) * bobHeight; transform.position = _startPos + Vector3.up * y; }} private void OnTriggerEnter(Collider other) {{ if (!other.CompareTag("Player")) return; var receiver = other.GetComponent(); if (receiver == null || !receiver.CanReceive(type)) return; Apply(receiver); if (collectSound != null && _audio != null) _audio.PlayOneShot(collectSound); if (glowParticles != null) glowParticles.Play(); if (respawn) Invoke(nameof(Reactivate), respawnDelay); gameObject.SetActive(false); }} private void Apply(IPickupReceiver r) {{ switch (type) {{ case PickupType.Health: r.AddHealth(amount); break; case PickupType.Money: r.AddMoney(amount); break; case PickupType.Ammo: r.AddAmmo(amount); break; case PickupType.Armor: r.AddArmor(amount); break; case PickupType.Weapon: r.AddWeapon(weaponId, amount); break; }} }} private void Reactivate() {{ gameObject.SetActive(true); if (glowParticles != null) glowParticles.Stop(); }} }} public interface IPickupReceiver {{ bool CanReceive(CreatePickupSystemTool.PickupType type); void AddHealth(int amount); void AddMoney(int amount); void AddAmmo(int amount); void AddArmor(int amount); void AddWeapon(string weaponId, int ammo); }} }} ''').replace("CreatePickupSystemTool.PickupType", "Pickup.PickupType") # =========================================================================== # 9. Health system # =========================================================================== @register_tool class CreateHealthSystemTool(BaseUnityTool): """Generate a health system with regen, armor, damage types and respawn.""" name = "create_health_system" description = ( "Generate a health system with health regen, armor, damage types, " "death screen and respawn." ) input_schema = { "type": "object", "properties": { "class_name": {"type": "string", "default": "HealthSystem"}, "max_health": {"type": "number", "default": 100.0}, }, } def run(self, class_name: str = "HealthSystem", max_health: float = 100.0, **_: Any) -> str: return self._wrap(f''' using System; using UnityEngine; namespace OpenWorld.Player {{ /// /// Player health & armor manager. Tracks health, armor, damage type /// resistances, regen-after-delay, death state and respawn flow. /// Exposes the IDamageable interface used by weapons and NPCs. /// [DisallowMultipleComponent] public class {class_name} : MonoBehaviour, IDamageable {{ public enum DamageType {{ Bullet, Fire, Explosion, Fall, Melee, Drown }} [Header("Health")] [SerializeField] private float maxHealth = {max_health}f; [SerializeField] private float maxArmor = 100f; [Header("Regen")] [SerializeField] private float regenDelay = 5f; [SerializeField] private float regenRate = 8f; [SerializeField] private float regenThreshold = 30f; [Header("Resistances")] [SerializeField] private float armorAbsorb = 0.5f; [SerializeField] private float fallThreshold = 8f; [Header("Respawn")] [SerializeField] private Transform respawnPoint; [SerializeField] private float respawnDelay = 4f; [SerializeField] private GameObject deathScreen; public float Health {{ get; private set; }} public float Armor {{ get; private set; }} public bool IsDead {{ get; private set; }} public event Action OnHealthChanged; // health, armor public event Action OnDeath; public event Action OnRespawn; private float _regenTimer; private float _respawnTimer; private void Awake() {{ Health = maxHealth; Armor = 0f; OnHealthChanged?.Invoke(Health, Armor); }} private void Update() {{ if (IsDead) {{ _respawnTimer -= Time.deltaTime; if (_respawnTimer <= 0f) Respawn(); return; }} TryRegen(); }} private void TryRegen() {{ if (Health >= maxHealth) return; _regenTimer -= Time.deltaTime; if (_regenTimer > 0f) return; Health = Mathf.Clamp(Health + regenRate * Time.deltaTime, 0f, maxHealth); OnHealthChanged?.Invoke(Health, Armor); }} public void TakeDamage(float amount, Vector3 source) {{ TakeDamage(amount, DamageType.Bullet, source); }} public void TakeDamage(float amount, DamageType type, Vector3 source) {{ if (IsDead) return; float dmg = ApplyResistances(amount, type); if (Armor > 0f) {{ float absorbed = Mathf.Min(Armor, dmg * armorAbsorb); Armor -= absorbed; dmg -= absorbed; }} Health = Mathf.Max(0f, Health - dmg); _regenTimer = regenDelay; OnHealthChanged?.Invoke(Health, Armor); if (Health <= 0f) Die(); }} protected virtual float ApplyResistances(float amount, DamageType type) {{ return type switch {{ DamageType.Fire => amount * 0.7f, DamageType.Explosion => amount * 1.2f, DamageType.Melee => amount * 0.8f, _ => amount, }}; }} public void Heal(float amount) {{ Health = Mathf.Clamp(Health + amount, 0f, maxHealth); OnHealthChanged?.Invoke(Health, Armor); }} public void AddArmor(float amount) {{ Armor = Mathf.Clamp(Armor + amount, 0f, maxArmor); OnHealthChanged?.Invoke(Health, Armor); }} private void Die() {{ IsDead = true; _respawnTimer = respawnDelay; if (deathScreen != null) deathScreen.SetActive(true); OnDeath?.Invoke(); }} private void Respawn() {{ IsDead = false; Health = maxHealth; Armor = 0f; if (respawnPoint != null) transform.position = respawnPoint.position; if (deathScreen != null) deathScreen.SetActive(false); OnHealthChanged?.Invoke(Health, Armor); OnRespawn?.Invoke(); }} private void OnCollisionEnter(Collision c) {{ if (c.relativeVelocity.y < -fallThreshold) TakeDamage(Mathf.Abs(c.relativeVelocity.y) * 4f, DamageType.Fall, transform.position); }} }} public interface IDamageable {{ void TakeDamage(float amount, Vector3 source); }} }} ''') # =========================================================================== # 10. Weapon system # =========================================================================== @register_tool class CreateWeaponSystemTool(BaseUnityTool): """Generate a weapon system supporting multiple weapon archetypes.""" name = "create_weapon_system" description = ( "Generate a weapon system supporting pistol, rifle, shotgun, SMG " "and rocket launcher with muzzle flash, recoil, spread, reload " "and ammo." ) input_schema = { "type": "object", "properties": { "class_name": {"type": "string", "default": "WeaponSystem"}, }, } def run(self, class_name: str = "WeaponSystem", **_: Any) -> str: return self._wrap(f''' using System; using System.Collections.Generic; using UnityEngine; namespace OpenWorld.Combat {{ /// /// Weapon controller supporting multiple archetypes (Pistol, Rifle, /// Shotgun, SMG, RocketLauncher). Each weapon defines fire rate, /// damage, spread, mag size, reload time, recoil curve and audio. /// Ammo is tracked per-weapon with a shared reserve. /// public class {class_name} : MonoBehaviour {{ [Serializable] public enum WeaponType {{ Pistol, Rifle, Shotgun, SMG, RocketLauncher }} [Serializable] public class WeaponData {{ public string id = "pistol"; public WeaponType type = WeaponType.Pistol; public float damage = 25f; public float fireRate = 6f; // shots/sec public int magSize = 12; public int reserveAmmo = 96; public float reloadTime = 1.5f; public float spread = 1.5f; public float range = 100f; public float recoil = 1.2f; public int pellets = 1; public AudioClip fireSound; public AudioClip reloadSound; public GameObject muzzleFlashPrefab; public GameObject projectilePrefab; // for rockets }} [Header("Setup")] [SerializeField] private List weapons = new List(); [SerializeField] private Camera cam; [SerializeField] private Transform muzzlePoint; [SerializeField] private LayerMask hitMask = ~0; [SerializeField] private AudioSource audioSource; private int _currentIndex; private WeaponData _current; private int _magAmmo; private float _nextFireTime; private float _reloadEndTime; private bool _reloading; public WeaponData CurrentWeapon => _current; public int CurrentMagAmmo => _magAmmo; public bool IsReloading => _reloading; public event Action OnWeaponChanged; // mag, reserve, id private void Awake() {{ if (cam == null) cam = Camera.main; if (weapons.Count > 0) SelectWeapon(0); }} private void Update() {{ if (weapons.Count == 0) return; HandleSwitch(); if (_reloading) {{ if (Time.time >= _reloadEndTime) FinishReload(); return; }} if (Input.GetKeyDown(KeyCode.R)) StartReload(); if (Input.GetMouseButton(0) && Time.time >= _nextFireTime) Fire(); }} private void HandleSwitch() {{ if (Input.GetKeyDown(KeyCode.Alpha1)) SelectWeapon(0); if (Input.GetKeyDown(KeyCode.Alpha2)) SelectWeapon(1); if (Input.GetKeyDown(KeyCode.Alpha3)) SelectWeapon(2); if (Input.GetKeyDown(KeyCode.Alpha4)) SelectWeapon(3); if (Input.GetKeyDown(KeyCode.Alpha5)) SelectWeapon(4); }} public void SelectWeapon(int index) {{ if (index < 0 || index >= weapons.Count) return; _currentIndex = index; _current = weapons[index]; _magAmmo = _current.magSize; _reloading = false; OnWeaponChanged?.Invoke(_magAmmo, _current.reserveAmmo, _current.id); }} private void Fire() {{ if (_magAmmo <= 0) {{ StartReload(); return; }} _magAmmo--; _nextFireTime = Time.time + 1f / _current.fireRate; PlayFireEffects(); ApplyRecoil(); for (int i = 0; i < _current.pellets; i++) FireRay(); if (_current.type == WeaponType.RocketLauncher) FireProjectile(); OnWeaponChanged?.Invoke(_magAmmo, _current.reserveAmmo, _current.id); }} private void FireRay() {{ Vector3 dir = cam.transform.forward; dir = Quaternion.Euler( UnityEngine.Random.Range(-_current.spread, _current.spread), UnityEngine.Random.Range(-_current.spread, _current.spread), 0f) * dir; if (Physics.Raycast(cam.transform.position, dir, out RaycastHit hit, _current.range, hitMask)) {{ var dmg = hit.collider.GetComponent(); dmg?.TakeDamage(_current.damage, hit.point); SpawnImpact(hit); }} }} private void FireProjectile() {{ if (_current.projectilePrefab == null || muzzlePoint == null) return; Instantiate(_current.projectilePrefab, muzzlePoint.position, Quaternion.LookRotation(cam.transform.forward)); }} private void PlayFireEffects() {{ if (audioSource != null && _current.fireSound != null) audioSource.PlayOneShot(_current.fireSound); if (_current.muzzleFlashPrefab != null && muzzlePoint != null) {{ GameObject flash = Instantiate(_current.muzzleFlashPrefab, muzzlePoint); Destroy(flash, 0.08f); }} }} private void ApplyRecoil() {{ // Camera recoil handled by camera controller via event. transform.localPosition -= Vector3.forward * _current.recoil * 0.05f; }} private void SpawnImpact(RaycastHit hit) {{ // Subclass or pool impact effects. }} private void StartReload() {{ if (_reloading || _magAmmo == _current.magSize || _current.reserveAmmo <= 0) return; _reloading = true; _reloadEndTime = Time.time + _current.reloadTime; if (audioSource != null && _current.reloadSound != null) audioSource.PlayOneShot(_current.reloadSound); }} private void FinishReload() {{ _reloading = false; int needed = _current.magSize - _magAmmo; int taken = Mathf.Min(needed, _current.reserveAmmo); _magAmmo += taken; _current.reserveAmmo -= taken; OnWeaponChanged?.Invoke(_magAmmo, _current.reserveAmmo, _current.id); }} }} }} ''') # =========================================================================== # 11. Audio manager # =========================================================================== @register_tool class CreateAudioManagerTool(BaseUnityTool): """Generate a 3D audio manager with music, SFX and radio stations.""" name = "create_audio_manager" description = ( "Generate a 3D audio manager with spatial SFX, engine sounds, " "gunshots, footsteps, radio stations and ambient beds." ) input_schema = { "type": "object", "properties": { "class_name": {"type": "string", "default": "AudioManager"}, }, } def run(self, class_name: str = "AudioManager", **_: Any) -> str: return self._wrap(f''' using System; using System.Collections.Generic; using UnityEngine; namespace OpenWorld.Audio {{ /// /// Central audio service. Maintains pools for SFX, a music bus, an /// ambient bed and multiple radio stations. Spatial 3D sources are /// created on demand for one-shot SFX; looping engine sounds are /// tracked per-vehicle. /// public class {class_name} : MonoBehaviour {{ public static {class_name} Instance {{ get; private set; }} [Header("Buses")] [SerializeField] private AudioSource sfxPrefab; [SerializeField] private AudioSource musicSource; [SerializeField] private AudioSource ambientSource; [SerializeField] private int sfxPoolSize = 32; [Header("Radio Stations")] [SerializeField] private RadioStation[] stations; [SerializeField] private AudioSource radioSource; [Serializable] public class RadioStation {{ public string name; public AudioClip[] songs; [NonSerialized] public int currentIndex; }} private readonly List _pool = new List(); private readonly Dictionary _looping = new Dictionary(); private int _currentStation = -1; private void Awake() {{ Instance = this; for (int i = 0; i < sfxPoolSize; i++) {{ AudioSource src = Instantiate(sfxPrefab, transform); src.gameObject.SetActive(false); _pool.Add(src); }} }} public void PlaySfx(AudioClip clip, Vector3 position, float volume = 1f, float pitch = 1f) {{ AudioSource src = GetFreeSource(); if (src == null) return; src.transform.position = position; src.clip = clip; src.volume = volume; src.pitch = pitch; src.spatialBlend = 1f; src.gameObject.SetActive(true); src.Play(); }} public int PlayLooping(AudioClip clip, Vector3 position, float volume = 1f) {{ AudioSource src = GetFreeSource(); if (src == null) return -1; src.transform.position = position; src.clip = clip; src.volume = volume; src.loop = true; src.spatialBlend = 1f; src.gameObject.SetActive(true); src.Play(); int id = src.GetInstanceID(); _looping[id] = src; return id; }} public void StopLooping(int id) {{ if (_looping.TryGetValue(id, out AudioSource src)) {{ src.Stop(); src.loop = false; src.gameObject.SetActive(false); _looping.Remove(id); }} }} public void UpdateLoopingPosition(int id, Vector3 pos) {{ if (_looping.TryGetValue(id, out AudioSource src)) src.transform.position = pos; }} public void PlayMusic(AudioClip clip) {{ if (musicSource == null) return; musicSource.clip = clip; musicSource.Play(); }} public void SetAmbient(AudioClip clip) {{ if (ambientSource == null) return; ambientSource.clip = clip; ambientSource.loop = true; ambientSource.Play(); }} public void PlayRadio(int stationIndex) {{ if (radioSource == null || stations == null || stationIndex < 0 || stationIndex >= stations.Length) return; _currentStation = stationIndex; PlayCurrentSong(); }} public void NextSong() {{ if (_currentStation < 0) return; stations[_currentStation].currentIndex = (stations[_currentStation].currentIndex + 1) % stations[_currentStation].songs.Length; PlayCurrentSong(); }} public void StopRadio() {{ _currentStation = -1; if (radioSource != null) radioSource.Stop(); }} private void PlayCurrentSong() {{ var s = stations[_currentStation]; radioSource.clip = s.songs[s.currentIndex]; radioSource.Play(); }} private AudioSource GetFreeSource() {{ foreach (var src in _pool) if (!src.isPlaying) return src; return null; }} private void Update() {{ if (_currentStation >= 0 && radioSource != null && !radioSource.isPlaying) NextSong(); }} }} }} ''') # =========================================================================== # 12. UI manager # =========================================================================== @register_tool class CreateUiManagerTool(BaseUnityTool): """Generate a HUD UI manager with health bar, minimap, money, wanted.""" name = "create_ui_manager" description = ( "Generate a UI manager with health bar, minimap, money counter, " "wanted level stars, weapon wheel and phone." ) input_schema = { "type": "object", "properties": { "class_name": {"type": "string", "default": "UiManager"}, }, } def run(self, class_name: str = "UiManager", **_: Any) -> str: return self._wrap(f''' using System; using System.Collections.Generic; using UnityEngine; using UnityEngine.UI; namespace OpenWorld.UI {{ /// /// Central HUD manager. Subscribes to gameplay events and updates /// health/armor bars, money counter, wanted-level stars, weapon label /// and the in-game phone. Weapon wheel is shown on tab hold. /// public class {class_name} : MonoBehaviour {{ [Header("Health")] [SerializeField] private Image healthBar; [SerializeField] private Image armorBar; [SerializeField] private Text healthText; [Header("Money")] [SerializeField] private Text moneyText; [Header("Wanted")] [SerializeField] private Image[] wantedStars; [SerializeField] private Color starOff = new Color(0.2f, 0.2f, 0.2f, 0.4f); [SerializeField] private Color starOn = Color.yellow; [Header("Weapon")] [SerializeField] private Text weaponNameText; [SerializeField] private Text ammoText; [SerializeField] private GameObject weaponWheel; [SerializeField] private Transform wheelCenter; [SerializeField] private float wheelRadius = 120f; [Header("Phone")] [SerializeField] private GameObject phoneRoot; [SerializeField] private CanvasGroup phoneCanvas; [Header("Minimap")] [SerializeField] private RawImage minimapImage; private int _money; private int _wanted; public int Money {{ get => _money; set {{ _money = Mathf.Max(0, value); moneyText.text = "$" + _money.ToString("N0"); }} }} public int Wanted {{ get => _wanted; set {{ _wanted = Mathf.Clamp(value, 0, wantedStars.Length); for (int i = 0; i < wantedStars.Length; i++) wantedStars[i].color = i < _wanted ? starOn : starOff; }} }} private void Awake() {{ if (weaponWheel != null) weaponWheel.SetActive(false); if (phoneRoot != null) phoneRoot.SetActive(false); }} private void Update() {{ HandleWeaponWheel(); HandlePhone(); }} public void SetHealth(float current, float max) {{ if (healthBar != null) healthBar.fillAmount = current / max; if (healthText != null) healthText.text = Mathf.CeilToInt(current) + " / " + Mathf.CeilToInt(max); }} public void SetArmor(float current, float max) {{ if (armorBar != null) armorBar.fillAmount = max > 0 ? current / max : 0f; }} public void SetWeapon(string name, int mag, int reserve) {{ if (weaponNameText != null) weaponNameText.text = name; if (ammoText != null) ammoText.text = mag + " / " + reserve; }} private void HandleWeaponWheel() {{ if (weaponWheel == null) return; bool show = Input.GetKey(KeyCode.Tab); weaponWheel.SetActive(show); if (show && wheelCenter != null) {{ Vector2 mouse = Input.mousePosition; Vector2 center = RectTransformUtility.WorldToScreenPoint(null, wheelCenter.position); Vector2 dir = (mouse - center).normalized; float angle = Mathf.Atan2(dir.y, dir.x) * Mathf.Rad2Deg; int slot = Mathf.RoundToInt(((angle + 360f) % 360f) / (360f / 8f)) % 8; if (Input.GetMouseButtonUp(0)) SelectWheelSlot(slot); }} }} private void SelectWheelSlot(int slot) {{ // Hook into WeaponSystem.SelectWeapon(slot). Debug.Log("Selected weapon slot " + slot); }} private void HandlePhone() {{ if (phoneRoot == null) return; if (Input.GetKeyDown(KeyCode.P)) {{ bool open = !phoneRoot.activeSelf; phoneRoot.SetActive(open); if (open) Time.timeScale = 0.3f; else Time.timeScale = 1f; }} }} }} }} ''') # =========================================================================== # 13. Terrain generator # =========================================================================== @register_tool class CreateTerrainGeneratorTool(BaseUnityTool): """Generate a heightmap terrain with roads, rivers and elevation.""" name = "create_terrain_generator" description = ( "Generate a heightmap terrain generator with roads, rivers and " "elevation noise." ) input_schema = { "type": "object", "properties": { "class_name": {"type": "string", "default": "TerrainGenerator"}, "size": {"type": "integer", "default": 1024}, "height": {"type": "number", "default": 600.0}, }, } def run(self, class_name: str = "TerrainGenerator", size: int = 1024, height: float = 600.0, **_: Any) -> str: return self._wrap(f''' using UnityEngine; namespace OpenWorld.World {{ /// /// Procedural terrain generator. Builds a TerrainData from layered /// Perlin noise (continental + hills + detail), carves a river spline /// and stamps a road network onto the heightmap. Splat prototypes are /// assigned by elevation/slope. /// public class {class_name} : MonoBehaviour {{ [Header("Dimensions")] [SerializeField] private int size = {size}; [SerializeField] private float worldHeight = {height}f; [SerializeField] private int resolution = 513; [Header("Noise")] [SerializeField] private float continentScale = 0.003f; [SerializeField] private float hillsScale = 0.01f; [SerializeField] private float detailScale = 0.05f; [SerializeField] private float continentWeight = 0.7f; [SerializeField] private float hillsWeight = 0.25f; [SerializeField] private float detailWeight = 0.05f; [SerializeField] private int seed = 1; [Header("River")] [SerializeField] private AnimationCurve riverFalloff; [SerializeField] private float riverWidth = 30f; [SerializeField] private float riverDepth = 12f; [Header("Textures")] [SerializeField] private Texture2D grassTexture; [SerializeField] private Texture2D rockTexture; [SerializeField] private Texture2D sandTexture; public Terrain Generated {{ get; private set; }} public void Generate() {{ TerrainData data = new TerrainData(); data.heightmapResolution = resolution; data.size = new Vector3(size, worldHeight, size); data.baseMapResolution = 1024; data.SetDetailResolution(1024, 8); float[,] heights = new float[resolution, resolution]; BuildHeights(heights); CarveRiver(heights); data.SetHeights(0, 0, heights); ApplyTextures(data, heights); Terrain terrain = Terrain.CreateTerrainGameObject(data); terrain.transform.SetParent(transform); Generated = terrain; }} private void BuildHeights(float[,] heights) {{ float offsetX = seed * 137.13f; float offsetZ = seed * 71.7f; for (int z = 0; z < resolution; z++) for (int x = 0; x < resolution; x++) {{ float c = Mathf.PerlinNoise(x * continentScale + offsetX, z * continentScale + offsetZ); float h = Mathf.PerlinNoise(x * hillsScale + offsetX, z * hillsScale + offsetZ); float d = Mathf.PerlinNoise(x * detailScale + offsetX, z * detailScale + offsetZ); heights[z, x] = c * continentWeight + h * hillsWeight + d * detailWeight; }} }} private void CarveRiver(float[,] heights) {{ // River meanders diagonally across the map. for (int z = 0; z < resolution; z++) {{ float t = z / (float)resolution; int xc = Mathf.RoundToInt((Mathf.Sin(t * Mathf.PI * 4f) * 0.15f + 0.5f) * resolution); for (int x = xc - (int)riverWidth; x < xc + (int)riverWidth; x++) {{ if (x < 0 || x >= resolution) continue; float dist = Mathf.Abs(x - xc) / riverWidth; float carve = riverFalloff != null ? riverFalloff.Evaluate(dist) : 1f - dist; heights[z, x] = Mathf.Max(0f, heights[z, x] - carve * riverDepth / worldHeight); }} }} }} private void ApplyTextures(TerrainData data, float[,] heights) {{ if (grassTexture == null) return; var proto = new[] {{ new TerrainLayer {{ diffuseTexture = grassTexture, tileSize = new Vector2(8, 8) }}, new TerrainLayer {{ diffuseTexture = rockTexture, tileSize = new Vector2(8, 8) }}, new TerrainLayer {{ diffuseTexture = sandTexture, tileSize = new Vector2(8, 8) }}, }}; data.terrainLayers = proto; float[,,] map = new float[resolution, resolution, proto.Length]; for (int z = 0; z < resolution; z++) for (int x = 0; x < resolution; x++) {{ float h = heights[z, x]; if (h < 0.05f) {{ map[z, x, 2] = 1f; }} // sand near water else if (h > 0.6f) {{ map[z, x, 1] = 1f; }} // rock up high else {{ map[z, x, 0] = 1f; }} // grass }} data.SetAlphamaps(0, 0, map); }} }} }} ''') # =========================================================================== # 14. Water shader # =========================================================================== @register_tool class CreateWaterShaderTool(BaseUnityTool): """Generate an animated water surface shader and controller.""" name = "create_water_shader" description = "Generate an animated water shader with reflections and waves." input_schema = { "type": "object", "properties": { "shader_name": {"type": "string", "default": "OpenWorld/Water"}, }, } def run(self, shader_name: str = "OpenWorld/Water", **_: Any) -> str: shader_code = self._wrap(f''' Shader "{shader_name}" {{ Properties {{ _MainColor ("Main Color", Color) = (0.1, 0.4, 0.7, 0.8) _DeepColor ("Deep Color", Color) = (0.02, 0.1, 0.25, 1.0) _WaveSpeed ("Wave Speed", Float) = 1.0 _WaveHeight ("Wave Height", Float) = 0.2 _WaveTiling ("Wave Tiling", Float) = 4.0 _Glossiness ("Smoothness", Range(0,1)) = 0.9 _FresnelPower ("Fresnel Power", Float) = 3.0 }} SubShader {{ Tags {{ "RenderType"="Transparent" "Queue"="Transparent" }} LOD 200 Blend SrcAlpha OneMinusSrcAlpha CGPROGRAM #pragma surface surf Standard alpha vertex:vert #pragma target 3.0 struct Input {{ float2 uv_MainTex; float3 worldPos; float3 viewDir; }}; fixed4 _MainColor; fixed4 _DeepColor; float _WaveSpeed; float _WaveHeight; float _WaveTiling; half _Glossiness; float _FresnelPower; float Wave(float2 p, float t) {{ return sin(p.x * _WaveTiling + t * _WaveSpeed) * 0.5 + sin(p.y * _WaveTiling * 1.3 + t * _WaveSpeed * 0.8) * 0.5; }} void vert(inout appdata_full v) {{ float t = _Time.y; float w = Wave(v.texcoord.xy * 10.0, t); v.vertex.y += w * _WaveHeight; v.normal = normalize(float3(-cos(v.texcoord.x * 10.0 * _WaveTiling + t * _WaveSpeed) * _WaveHeight * _WaveTiling, 1.0, 0.0)); }} void surf(Input IN, inout SurfaceOutputStandard o) {{ float t = _Time.y; float w = Wave(IN.uv_MainTex * 10.0, t); fixed4 c = lerp(_DeepColor, _MainColor, w * 0.5 + 0.5); float fres = pow(1.0 - saturate(dot(normalize(IN.viewDir), float3(0,1,0))), _FresnelPower); o.Albedo = c.rgb; o.Alpha = c.a + fres * 0.3; o.Metallic = 0.0; o.Smoothness = _Glossiness; o.Emission = fres * 0.2; }} ENDCG }} FallBack "Transparent/VertexLit" }} ''') controller = self._wrap(''' using UnityEngine; namespace OpenWorld.World { /// /// Drives the animated water material — adjusts wave height by weather /// and toggles reflection probe refresh on demand. /// [ExecuteAlways] public class WaterController : MonoBehaviour { [SerializeField] private Material waterMaterial; [SerializeField] private float calmHeight = 0.15f; [SerializeField] private float stormHeight = 0.6f; [SerializeField] private float transitionSpeed = 0.5f; private float _currentHeight = 0.15f; private float _targetHeight = 0.15f; public float StormFactor { set => _targetHeight = Mathf.Lerp(calmHeight, stormHeight, Mathf.Clamp01(value)); } private void Update() { _currentHeight = Mathf.Lerp(_currentHeight, _targetHeight, transitionSpeed * Time.deltaTime); if (waterMaterial != null) waterMaterial.SetFloat("_WaveHeight", _currentHeight); } } } ''') return f"// === SHADER ===\n{shader_code}\n// === CONTROLLER ===\n{controller}" # =========================================================================== # 15. Particle effects # =========================================================================== @register_tool class CreateParticleEffectsTool(BaseUnityTool): """Generate a particle effect factory for explosions, blood, smoke.""" name = "create_particle_effects" description = ( "Generate a particle effect factory: explosions, blood, smoke, " "sparks, muzzle flash, tire smoke." ) input_schema = { "type": "object", "properties": { "class_name": {"type": "string", "default": "ParticleEffectFactory"}, }, } def run(self, class_name: str = "ParticleEffectFactory", **_: Any) -> str: return self._wrap(f''' using System.Collections.Generic; using UnityEngine; namespace OpenWorld.FX {{ /// /// Pool-backed factory for one-shot particle effects. Configure prefabs /// per effect type and call at world position. The /// factory pools instances to avoid runtime GC. /// public class {class_name} : MonoBehaviour {{ public enum EffectType {{ Explosion, Blood, Smoke, Sparks, MuzzleFlash, TireSmoke, Debris, Fire }} [System.Serializable] public class EffectEntry {{ public EffectType type; public ParticleSystem prefab; public int prewarmCount = 4; }} [SerializeField] private EffectEntry[] entries; private readonly Dictionary> _pools = new Dictionary>(); private readonly Dictionary _prefabs = new Dictionary(); public static {class_name} Instance {{ get; private set; }} private void Awake() {{ Instance = this; foreach (var e in entries) {{ _prefabs[e.type] = e.prefab; _pools[e.type] = new Queue(); for (int i = 0; i < e.prewarmCount; i++) _pools[e.type].Enqueue(CreateInstance(e.type)); }} }} public void Spawn(EffectType type, Vector3 pos, Quaternion rot = default) {{ if (!_pools.TryGetValue(type, out var pool) || pool.Count == 0) pool.Enqueue(CreateInstance(type)); ParticleSystem ps = pool.Dequeue(); ps.transform.SetPositionAndRotation(pos, rot == default ? Quaternion.identity : rot); ps.gameObject.SetActive(true); ps.Play(); StartCoroutine(ReturnAfter(ps, pool, ps.main.duration)); }} private System.Collections.IEnumerator ReturnAfter(ParticleSystem ps, Queue pool, float delay) {{ yield return new WaitForSeconds(delay + 0.5f); ps.Stop(true, ParticleSystemStopBehavior.StopEmittingAndClear); ps.gameObject.SetActive(false); pool.Enqueue(ps); }} private ParticleSystem CreateInstance(EffectType type) {{ if (!_prefabs.TryGetValue(type, out var prefab)) return null; ParticleSystem ps = Instantiate(prefab, transform); ps.gameObject.SetActive(false); return ps; }} public void SpawnExplosion(Vector3 pos, float radius) {{ Spawn(EffectType.Explosion, pos); Spawn(EffectType.Fire, pos); Spawn(EffectType.Debris, pos); Collider[] hits = Physics.OverlapSphere(pos, radius); foreach (var h in hits) {{ var dmg = h.GetComponent(); dmg?.TakeDamage(80f * (1f - Vector3.Distance(pos, h.transform.position) / radius), pos); if (h.TryGetComponent(out var rb)) rb.AddExplosionForce(800f, pos, radius); }} }} }} }} ''') # =========================================================================== # 16. Save system # =========================================================================== @register_tool class CreateSaveSystemTool(BaseUnityTool): """Generate a JSON save/load system with player, vehicles and inventory.""" name = "create_save_system" description = ( "Generate a JSON save/load system storing position, health, money, " "weapons and vehicles." ) input_schema = { "type": "object", "properties": { "class_name": {"type": "string", "default": "SaveSystem"}, }, } def run(self, class_name: str = "SaveSystem", **_: Any) -> str: return self._wrap(f''' using System; using System.Collections.Generic; using System.IO; using UnityEngine; namespace OpenWorld.Persistence {{ /// /// JSON-based save/load. Serializes player state, money, weapons, /// inventory and a list of parked vehicles to Application.persistentDataPath. /// Slot-based (0-9). Exposes events for UI hooks. /// public class {class_name} : MonoBehaviour {{ public static {class_name} Instance {{ get; private set; }} [Serializable] public class WeaponSave {{ public string id; public int magAmmo; public int reserveAmmo; }} [Serializable] public class VehicleSave {{ public string model; public float x, y, z, qx, qy, qz, qw; public float health; public string color; }} [Serializable] public class GameSave {{ public int slot; public string scene; public float px, py, pz; public float health, armor; public int money; public List weapons = new List(); public List vehicles = new List(); public long timestampTicks; }} public event Action OnSaveComplete; public event Action OnLoadComplete; private void Awake() {{ Instance = this; }} public void Save(int slot, GameSave data) {{ data.slot = slot; data.timestampTicks = DateTime.UtcNow.Ticks; string json = JsonUtility.ToJson(data, true); string path = PathFor(slot); File.WriteAllText(path, json); OnSaveComplete?.Invoke(slot); }} public GameSave Load(int slot) {{ string path = PathFor(slot); if (!File.Exists(path)) return null; string json = File.ReadAllText(path); GameSave data = JsonUtility.FromJson(json); OnLoadComplete?.Invoke(slot); return data; }} public bool HasSave(int slot) => File.Exists(PathFor(slot)); public void Delete(int slot) {{ string path = PathFor(slot); if (File.Exists(path)) File.Delete(path); }} private static string PathFor(int slot) => Path.Combine(Application.persistentDataPath, $"save_{{slot}}.json"); public static Vector3 Vec(float x, float y, float z) => new Vector3(x, y, z); }} }} ''') # =========================================================================== # 17. Inventory system # =========================================================================== @register_tool class CreateInventorySystemTool(BaseUnityTool): """Generate a grid inventory with drag-drop, weight and quick slots.""" name = "create_inventory_system" description = ( "Generate a grid inventory system with drag-drop, weight, and " "quick slots." ) input_schema = { "type": "object", "properties": { "class_name": {"type": "string", "default": "InventorySystem"}, "grid_width": {"type": "integer", "default": 8}, "grid_height": {"type": "integer", "default": 6}, }, } def run(self, class_name: str = "InventorySystem", grid_width: int = 8, grid_height: int = 6, **_: Any) -> str: return self._wrap(f''' using System; using System.Collections.Generic; using UnityEngine; namespace OpenWorld.Inventory {{ /// /// Grid-based inventory. Items occupy a rectangular footprint on the /// grid; weight and stack counts are enforced. Up to 8 quick-slots /// can be bound for hotkey use. The UI layer reads this data to render /// drag/drop icons. /// [Serializable] public class {class_name} : MonoBehaviour {{ [Serializable] public class Item {{ public string id; public string displayName; public int width = 1; public int height = 1; public int stack = 1; public int maxStack = 99; public float weight = 0.1f; public Sprite icon; public bool consumable; }} [Header("Grid")] [SerializeField] private int gridWidth = {grid_width}; [SerializeField] private int gridHeight = {grid_height}; [SerializeField] private float maxWeight = 80f; [Header("Quick Slots")] [SerializeField] private int quickSlotCount = 8; private readonly List _items = new List(); private readonly Item[] _quickSlots; public float CurrentWeight {{ get; private set; }} public IReadOnlyList Items => _items; public event Action OnChanged; public {class_name}() {{ _quickSlots = new Item[quickSlotCount]; }} public bool Add(Item item) {{ if (CurrentWeight + item.weight > maxWeight) return false; Item existing = _items.Find(i => i.id == item.id && i.stack < i.maxStack); if (existing != null && existing.width == item.width && existing.height == item.height) {{ int can = Mathf.Min(item.stack, existing.maxStack - existing.stack); existing.stack += can; item.stack -= can; }} if (item.stack > 0) {{ if (_items.Count >= gridWidth * gridHeight) return false; _items.Add(item); }} CurrentWeight += item.weight * item.stack; OnChanged?.Invoke(); return true; }} public bool Remove(string id, int count = 1) {{ Item item = _items.Find(i => i.id == id); if (item == null || item.stack < count) return false; item.stack -= count; CurrentWeight -= item.weight * count; if (item.stack <= 0) _items.Remove(item); OnChanged?.Invoke(); return true; }} public bool AssignQuickSlot(int slot, Item item) {{ if (slot < 0 || slot >= _quickSlots.Length) return false; _quickSlots[slot] = item; OnChanged?.Invoke(); return true; }} public Item GetQuickSlot(int slot) => (slot >= 0 && slot < _quickSlots.Length) ? _quickSlots[slot] : null; public void UseQuickSlot(int slot) {{ Item item = GetQuickSlot(slot); if (item == null || !item.consumable) return; Remove(item.id, 1); }} public int Count(string id) {{ int total = 0; foreach (var i in _items) if (i.id == id) total += i.stack; return total; }} public void Sort() {{ _items.Sort((a, b) => string.Compare(a.displayName, b.displayName, StringComparison.Ordinal)); OnChanged?.Invoke(); }} }} }} ''') # =========================================================================== # 18. Quest system # =========================================================================== @register_tool class CreateQuestSystemTool(BaseUnityTool): """Generate a quest system with tracking, objectives and rewards.""" name = "create_quest_system" description = ( "Generate a quest system with mission tracking, objectives, " "rewards and mission markers." ) input_schema = { "type": "object", "properties": { "class_name": {"type": "string", "default": "QuestSystem"}, }, } def run(self, class_name: str = "QuestSystem", **_: Any) -> str: return self._wrap(f''' using System; using System.Collections.Generic; using UnityEngine; namespace OpenWorld.Quests {{ /// /// Quest/mission tracker. Quests contain ordered objectives; each /// objective has a type (Reach, Kill, Collect, Talk). Completion /// awards money/XP and triggers the next objective. Mission markers /// are exposed via events for the minimap. /// public class {class_name} : MonoBehaviour {{ public enum ObjectiveType {{ Reach, Kill, Collect, Talk, Escort, Survive }} [Serializable] public class Objective {{ public string id; public string description; public ObjectiveType type; public string targetId; public int requiredCount = 1; public int currentCount; public Vector3 markerPosition; public bool optional; [NonSerialized] public bool complete; }} [Serializable] public class Quest {{ public string id; public string title; public string giverId; public List objectives = new List(); public int moneyReward; public int xpReward; public bool accepted; public bool completed; public bool failed; }} [SerializeField] private List allQuests = new List(); private readonly List _active = new List(); public IReadOnlyList ActiveQuests => _active; public event Action OnQuestAccepted; public event Action OnObjectiveCompleted; public event Action OnQuestCompleted; public event Action OnMarkerUpdated; public void Accept(string questId) {{ Quest q = allQuests.Find(x => x.id == questId); if (q == null || q.accepted) return; q.accepted = true; _active.Add(q); OnQuestAccepted?.Invoke(q); UpdateMarker(q); }} public void Progress(string objectiveId, int amount = 1, string targetId = null) {{ foreach (var q in _active) {{ if (q.completed) continue; foreach (var o in q.objectives) {{ if (o.complete || o.id != objectiveId) continue; if (!string.IsNullOrEmpty(targetId) && o.targetId != targetId) continue; o.currentCount = Mathf.Min(o.requiredCount, o.currentCount + amount); if (o.currentCount >= o.requiredCount) {{ o.complete = true; OnObjectiveCompleted?.Invoke(q, o); if (AllObjectivesComplete(q)) CompleteQuest(q); else UpdateMarker(q); }} }} }} }} private bool AllObjectivesComplete(Quest q) {{ foreach (var o in q.objectives) if (!o.optional && !o.complete) return false; return true; }} private void CompleteQuest(Quest q) {{ q.completed = true; _active.Remove(q); OnQuestCompleted?.Invoke(q); // Rewards dispatched by EconomySystem/SkillTree listeners. }} private void UpdateMarker(Quest q) {{ foreach (var o in q.objectives) if (!o.complete) {{ OnMarkerUpdated?.Invoke(o.markerPosition); return; }} }} public void FailQuest(string questId) {{ Quest q = _active.Find(x => x.id == questId); if (q == null) return; q.failed = true; _active.Remove(q); }} }} }} ''') # =========================================================================== # 19. Building generator # =========================================================================== @register_tool class CreateBuildingGeneratorTool(BaseUnityTool): """Generate a building generator with interiors, stairs and furniture.""" name = "create_building_generator" description = ( "Generate a building generator with interiors, stairs, elevators, " "rooms and furniture." ) input_schema = { "type": "object", "properties": { "class_name": {"type": "string", "default": "BuildingGenerator"}, "max_floors": {"type": "integer", "default": 8}, }, } def run(self, class_name: str = "BuildingGenerator", max_floors: int = 8, **_: Any) -> str: return self._wrap(f''' using System.Collections.Generic; using UnityEngine; namespace OpenWorld.World {{ /// /// Generates an enterable building. Floors are stacked with a central /// stairwell and an elevator shaft; rooms are subdivided per floor and /// optionally furnished from a prefab set. Exteriors are windowed. /// public class {class_name} : MonoBehaviour {{ [Header("Footprint")] [SerializeField] private Vector2Int footprint = new Vector2Int(20, 20); [SerializeField] private float floorHeight = 3.5f; [SerializeField] private int floors = {max_floors}; [Header("Prefabs")] [SerializeField] private GameObject floorPrefab; [SerializeField] private GameObject wallPrefab; [SerializeField] private GameObject stairPrefab; [SerializeField] private GameObject elevatorPrefab; [SerializeField] private GameObject[] furniturePrefabs; [SerializeField] private Material exteriorMaterial; [SerializeField] private Material interiorMaterial; private readonly List _parts = new List(); public void Generate() {{ Clear(); for (int f = 0; f < floors; f++) {{ Vector3 basePos = new Vector3(0, f * floorHeight, 0); BuildFloor(basePos, f); BuildStairwell(basePos, f); BuildElevator(basePos, f); SubdivideRooms(basePos, f); }} BuildExterior(); }} private void BuildFloor(Vector3 basePos, int floor) {{ GameObject slab = Instantiate(floorPrefab, basePos, Quaternion.identity, transform); slab.transform.localScale = new Vector3(footprint.x, 0.2f, footprint.y); _parts.Add(slab); }} private void BuildStairwell(Vector3 basePos, int floor) {{ Vector3 stairPos = basePos + new Vector3(-footprint.x * 0.4f, 0, 0); GameObject stair = Instantiate(stairPrefab, stairPos, Quaternion.identity, transform); _parts.Add(stair); }} private void BuildElevator(Vector3 basePos, int floor) {{ Vector3 elevPos = basePos + new Vector3(footprint.x * 0.4f, 0, 0); GameObject elev = Instantiate(elevatorPrefab, elevPos, Quaternion.identity, transform); _parts.Add(elev); }} private void SubdivideRooms(Vector3 basePos, int floor) {{ int roomsX = 2, roomsZ = 2; for (int rx = 0; rx < roomsX; rx++) for (int rz = 0; rz < roomsZ; rz++) {{ if (furniturePrefabs == null || furniturePrefabs.Length == 0) continue; Vector3 roomCenter = basePos + new Vector3( (rx - 0.5f) * footprint.x * 0.4f, 0, (rz - 0.5f) * footprint.y * 0.4f); GameObject furn = furniturePrefabs[Random.Range(0, furniturePrefabs.Length)]; Instantiate(furn, roomCenter, Quaternion.Euler(0, Random.Range(0, 360f), 0), transform); }} }} private void BuildExterior() {{ for (int f = 0; f < floors; f++) {{ Vector3 basePos = new Vector3(0, f * floorHeight + floorHeight * 0.5f, 0); GameObject wall = Instantiate(wallPrefab, basePos, Quaternion.identity, transform); wall.transform.localScale = new Vector3(footprint.x, floorHeight, 0.2f); if (exteriorMaterial != null) wall.GetComponent().sharedMaterial = exteriorMaterial; _parts.Add(wall); }} }} private void Clear() {{ foreach (var p in _parts) if (p != null) DestroyImmediate(p); _parts.Clear(); }} }} }} ''') # =========================================================================== # 20. Road network # =========================================================================== @register_tool class CreateRoadNetworkTool(BaseUnityTool): """Generate a road network with intersections, traffic lights and lanes.""" name = "create_road_network" description = ( "Generate a road network with highways, streets, intersections, " "traffic lights, lane markings and sidewalks." ) input_schema = { "type": "object", "properties": { "class_name": {"type": "string", "default": "RoadNetwork"}, }, } def run(self, class_name: str = "RoadNetwork", **_: Any) -> str: return self._wrap(f''' using System.Collections.Generic; using UnityEngine; namespace OpenWorld.World {{ /// /// Builds a graph of road nodes and segments, instantiating meshes for /// asphalt, lane markings, sidewalks and traffic lights at intersections. /// Lane data is exposed for the traffic system to follow. /// public class {class_name} : MonoBehaviour {{ public enum RoadClass {{ Highway, Arterial, Street, Alley }} [System.Serializable] public class RoadNode {{ public Vector3 position; public bool isIntersection; public TrafficLight light; }} [System.Serializable] public class RoadSegment {{ public RoadNode a, b; public RoadClass roadClass = RoadClass.Street; public int laneCount = 2; public float width = 8f; public LineRenderer markings; }} [System.Serializable] public class TrafficLight {{ public enum Phase {{ Red, Yellow, Green }} public Phase phase = Phase.Red; public float timer; public float redDuration = 8f; public float yellowDuration = 2f; public float greenDuration = 8f; public Light redLight, yellowLight, greenLight; }} [Header("Prefabs")] [SerializeField] private GameObject asphaltPrefab; [SerializeField] private GameObject sidewalkPrefab; [SerializeField] private GameObject trafficLightPrefab; [SerializeField] private Material markingMaterial; [Header("Nodes")] [SerializeField] private List nodes = new List(); [SerializeField] private List segments = new List(); public IReadOnlyList Segments => segments; public void BuildNetwork() {{ foreach (var seg in segments) BuildSegment(seg); foreach (var node in nodes) if (node.isIntersection) BuildIntersection(node); }} private void BuildSegment(RoadSegment seg) {{ Vector3 dir = seg.b.position - seg.a.position; float length = dir.magnitude; GameObject road = Instantiate(asphaltPrefab, seg.a.position + dir * 0.5f, Quaternion.LookRotation(dir), transform); road.transform.localScale = new Vector3(seg.width, 1f, length); BuildMarkings(seg); BuildSidewalks(seg, dir, length); }} private void BuildMarkings(RoadSegment seg) {{ GameObject marks = new GameObject("Markings"); marks.transform.SetParent(transform); var lr = marks.AddComponent(); lr.positionCount = 2; lr.SetPosition(0, seg.a.position + Vector3.up * 0.02f); lr.SetPosition(1, seg.b.position + Vector3.up * 0.02f); lr.startWidth = 0.2f; lr.endWidth = 0.2f; lr.material = markingMaterial; lr.numCornerVertices = 4; lr.numCapVertices = 4; seg.markings = lr; }} private void BuildSidewalks(RoadSegment seg, Vector3 dir, float length) {{ Vector3 normal = Vector3.Cross(Vector3.up, dir).normalized; Vector3 offset = normal * (seg.width * 0.5f + 1.5f); Vector3 center = seg.a.position + dir * 0.5f; Instantiate(sidewalkPrefab, center + offset, Quaternion.LookRotation(dir), transform) .transform.localScale = new Vector3(2f, 0.2f, length); Instantiate(sidewalkPrefab, center - offset, Quaternion.LookRotation(dir), transform) .transform.localScale = new Vector3(2f, 0.2f, length); }} private void BuildIntersection(RoadNode node) {{ Instantiate(asphaltPrefab, node.position, Quaternion.identity, transform) .transform.localScale = new Vector3(12f, 1f, 12f); if (trafficLightPrefab != null) {{ var tlObj = Instantiate(trafficLightPrefab, node.position + Vector3.up * 4f, Quaternion.identity, transform); node.light = tlObj.GetComponent(); }} }} private void Update() {{ foreach (var node in nodes) if (node.isIntersection && node.light != null) UpdateTrafficLight(node.light); }} private void UpdateTrafficLight(TrafficLight tl) {{ tl.timer -= Time.deltaTime; if (tl.timer > 0f) return; switch (tl.phase) {{ case TrafficLight.Phase.Red: tl.phase = TrafficLight.Phase.Green; tl.timer = tl.greenDuration; break; case TrafficLight.Phase.Green: tl.phase = TrafficLight.Phase.Yellow; tl.timer = tl.yellowDuration; break; case TrafficLight.Phase.Yellow: tl.phase = TrafficLight.Phase.Red; tl.timer = tl.redDuration; break; }} if (tl.redLight != null) tl.redLight.enabled = tl.phase == TrafficLight.Phase.Red; if (tl.yellowLight != null) tl.yellowLight.enabled = tl.phase == TrafficLight.Phase.Yellow; if (tl.greenLight != null) tl.greenLight.enabled = tl.phase == TrafficLight.Phase.Green; }} }} }} ''') # =========================================================================== # 21. Wanted system # =========================================================================== @register_tool class CreateWantedSystemTool(BaseUnityTool): """Generate a police wanted system with escalation and roadblocks.""" name = "create_wanted_system" description = ( "Generate a police wanted system with 1-5 stars, police cars, " "helicopters, roadblocks and escalation." ) input_schema = { "type": "object", "properties": { "class_name": {"type": "string", "default": "WantedSystem"}, }, } def run(self, class_name: str = "WantedSystem", **_: Any) -> str: return self._wrap(f''' using System; using System.Collections.Generic; using UnityEngine; namespace OpenWorld.AI {{ /// /// Police wanted-level system (1-5 stars). Crimes add heat; heat decays /// when the player is out of sight. Higher levels spawn additional /// units: cars at 2+, helicopters at 3+, roadblocks at 4+, SWAT at 5. /// public class {class_name} : MonoBehaviour {{ public static {class_name} Instance {{ get; private set; }} [Header("Heat")] [SerializeField] private float maxHeat = 100f; [SerializeField] private float heatDecayPerSecond = 4f; [SerializeField] private float[] starThresholds = {{ 0f, 15f, 35f, 55f, 80f }}; [Header("Spawning")] [SerializeField] private GameObject policeCarPrefab; [SerializeField] private GameObject helicopterPrefab; [SerializeField] private GameObject roadblockPrefab; [SerializeField] private GameObject swatPrefab; [SerializeField] private Transform player; [SerializeField] private float spawnRadius = 80f; [SerializeField] private float spawnInterval = 6f; [SerializeField] private int maxUnits = 16; public int Stars {{ get; private set; }} public float Heat {{ get; private set; }} public bool Hidden {{ get; set; }} public event Action OnStarsChanged; private readonly List _activeUnits = new List(); private float _spawnTimer; private void Awake() {{ Instance = this; }} private void Update() {{ DecayHeat(); UpdateStars(); ManageSpawns(); }} public void ReportCrime(float heat) {{ Heat = Mathf.Min(maxHeat, Heat + heat); Hidden = false; }} private void DecayHeat() {{ if (Hidden && Stars > 0) Heat = Mathf.Max(0f, Heat - heatDecayPerSecond * Time.deltaTime); }} private void UpdateStars() {{ int newStars = 0; for (int i = 0; i < starThresholds.Length; i++) if (Heat >= starThresholds[i]) newStars = i + 1; if (newStars == Stars) return; Stars = newStars; OnStarsChanged?.Invoke(Stars); }} private void ManageSpawns() {{ if (Stars == 0) {{ DespawnAll(); return; }} _spawnTimer -= Time.deltaTime; if (_spawnTimer > 0f) return; _spawnTimer = spawnInterval; int desired = Mathf.Min(maxUnits, Stars * 2); while (_activeUnits.Count < desired) SpawnUnit(); }} private void SpawnUnit() {{ Vector3 pos = player.position + UnityEngine.Random.insideUnitSphere * spawnRadius; pos.y = player.position.y; GameObject prefab = Stars switch {{ 5 => swatPrefab, 4 => roadblockPrefab, 3 => helicopterPrefab, _ => policeCarPrefab, }}; if (prefab == null) return; _activeUnits.Add(Instantiate(prefab, pos, Quaternion.identity)); }} private void DespawnAll() {{ foreach (var u in _activeUnits) if (u != null) Destroy(u); _activeUnits.Clear(); }} public void UnitDestroyed(GameObject unit) {{ if (_activeUnits.Remove(unit)) ReportCrime(5f); }} }} }} ''') # =========================================================================== # 22. Traffic system # =========================================================================== @register_tool class CreateTrafficSystemTool(BaseUnityTool): """Generate AI traffic cars that follow lanes and obey lights.""" name = "create_traffic_system" description = ( "Generate an AI traffic system: cars on lanes, traffic light " "obedience, lane following and accidents." ) input_schema = { "type": "object", "properties": { "class_name": {"type": "string", "default": "TrafficSystem"}, }, } def run(self, class_name: str = "TrafficSystem", **_: Any) -> str: return self._wrap(f''' using System.Collections.Generic; using UnityEngine; using UnityEngine.AI; namespace OpenWorld.AI {{ /// /// Spawns AI traffic vehicles that follow NavMesh paths along roads, /// stop at red lights and react to collisions. Each vehicle is a /// simplified kinematic car: it accelerates toward a target waypoint /// and brakes for obstacles ahead via a sphere cast. /// public class {class_name} : MonoBehaviour {{ [Header("Spawn")] [SerializeField] private GameObject[] vehiclePrefabs; [SerializeField] private Transform[] spawnPoints; [SerializeField] private int maxVehicles = 30; [SerializeField] private float spawnInterval = 3f; [SerializeField] private Transform player; [Header("Behaviour")] [SerializeField] private float cruiseSpeed = 12f; [SerializeField] private float detectionRange = 12f; [SerializeField] private LayerMask obstacleMask = ~0; private readonly List _vehicles = new List(); private float _spawnTimer; private void Update() {{ _spawnTimer -= Time.deltaTime; if (_spawnTimer <= 0f) {{ _spawnTimer = spawnInterval; if (_vehicles.Count < maxVehicles) SpawnVehicle(); }} for (int i = _vehicles.Count - 1; i >= 0; i--) {{ _vehicles[i].Tick(); if (_vehicles[i].Expired) {{ Destroy(_vehicles[i].gameObject); _vehicles.RemoveAt(i); }} }} }} private void SpawnVehicle() {{ if (vehiclePrefabs == null || vehiclePrefabs.Length == 0 || spawnPoints.Length == 0) return; Transform sp = spawnPoints[Random.Range(0, spawnPoints.Length)]; GameObject go = Instantiate(vehiclePrefabs[Random.Range(0, vehiclePrefabs.Length)], sp.position, sp.rotation); var v = go.AddComponent(); v.Initialize(this, cruiseSpeed, detectionRange, obstacleMask, player); _vehicles.Add(v); }} public bool IsRedLightAhead(Vector3 pos, Vector3 forward) {{ if (Physics.SphereCast(pos, 1f, forward, out RaycastHit hit, 6f, obstacleMask)) return hit.collider.CompareTag("TrafficLight_Red"); return false; }} }} public class TrafficVehicle : MonoBehaviour {{ private TrafficSystem _system; private float _speed; private float _cruise; private float _range; private LayerMask _mask; private Transform _player; private NavMeshAgent _agent; private Vector3 _target; private float _life = 120f; public bool Expired {{ get; private set; }} public void Initialize(TrafficSystem sys, float cruise, float range, LayerMask mask, Transform player) {{ _system = sys; _cruise = cruise; _range = range; _mask = mask; _player = player; _agent = gameObject.AddComponent(); _agent.speed = cruise; _agent.acceleration = 8f; _agent.angularSpeed = 180f; PickNewDestination(); }} private void PickNewDestination() {{ Vector3 rnd = transform.position + Random.insideUnitSphere * 60f; rnd.y = transform.position.y; _agent.SetDestination(rnd); _target = rnd; }} public void Tick() {{ _life -= Time.deltaTime; if (_life <= 0f || (_player != null && Vector3.Distance(transform.position, _player.position) > 200f)) Expired = true; if (_agent.remainingDistance < 4f) PickNewDestination(); if (ObstacleAhead() || _system.IsRedLightAhead(transform.position, transform.forward)) _speed = Mathf.Lerp(_speed, 0f, 4f * Time.deltaTime); else _speed = Mathf.Lerp(_speed, _cruise, 2f * Time.deltaTime); _agent.speed = _speed; }} private bool ObstacleAhead() {{ return Physics.SphereCast(transform.position + Vector3.up, 1.2f, transform.forward, out _, _range, _mask); }} private void OnCollisionEnter(Collision c) {{ if (c.relativeVelocity.magnitude > 8f) Expired = true; }} }} }} ''') # =========================================================================== # 23. Pedestrian system # =========================================================================== @register_tool class CreatePedestrianSystemTool(BaseUnityTool): """Generate walking pedestrians, crowds and panic behaviour.""" name = "create_pedestrian_system" description = ( "Generate a pedestrian system: walking NPCs, crowds, panic, " "conversations and reactions to the player." ) input_schema = { "type": "object", "properties": { "class_name": {"type": "string", "default": "PedestrianSystem"}, }, } def run(self, class_name: str = "PedestrianSystem", **_: Any) -> str: return self._wrap(f''' using System.Collections.Generic; using UnityEngine; using UnityEngine.AI; namespace OpenWorld.AI {{ /// /// Spawns and manages pedestrians in a radius around the player. Each /// pedestrian wanders, occasionally forms a conversation cluster with /// another, and flees on hearing gunshots/explosions. Crowd density is /// driven by the day/night hour. /// public class {class_name} : MonoBehaviour {{ [Header("Spawn")] [SerializeField] private GameObject[] pedestrianPrefabs; [SerializeField] private Transform player; [SerializeField] private float spawnRadius = 60f; [SerializeField] private float despawnRadius = 100f; [SerializeField] private int maxPedestrians = 80; [SerializeField] private float spawnInterval = 0.5f; [Header("Density")] [SerializeField] private AnimationCurve densityByHour = AnimationCurve.EaseInOut(0, 0.2f, 24, 0.2f); private readonly List _peds = new List(); private float _spawnTimer; public void SetHour(float hour) {{ float density = densityByHour.Evaluate(hour); maxPedestrians = Mathf.RoundToInt(80 * density); }} public void TriggerPanic(Vector3 source, float radius) {{ foreach (var p in _peds) {{ if (p == null) continue; if (Vector3.Distance(p.transform.position, source) < radius) p.GetComponent()?.Panic(source); }} }} private void Update() {{ _spawnTimer -= Time.deltaTime; if (_spawnTimer <= 0f) {{ _spawnTimer = spawnInterval; if (_peds.Count < maxPedestrians) SpawnPed(); }} for (int i = _peds.Count - 1; i >= 0; i--) {{ if (_peds[i] == null) {{ _peds.RemoveAt(i); continue; }} float dist = Vector3.Distance(_peds[i].transform.position, player.position); if (dist > despawnRadius) {{ Destroy(_peds[i]); _peds.RemoveAt(i); }} }} }} private void SpawnPed() {{ if (pedestrianPrefabs == null || pedestrianPrefabs.Length == 0) return; Vector3 pos = player.position + Random.insideUnitSphere * spawnRadius; pos.y = player.position.y; if (!NavMesh.SamplePosition(pos, out NavMeshHit hit, spawnRadius, NavMesh.AllAreas)) return; GameObject go = Instantiate(pedestrianPrefabs[Random.Range(0, pedestrianPrefabs.Length)], hit.position, Quaternion.identity); var ped = go.GetComponent(); if (ped == null) ped = go.AddComponent(); ped.Initialize(player); _peds.Add(go); }} }} public class Pedestrian : MonoBehaviour {{ private NavMeshAgent _agent; private Transform _player; private float _repath; private bool _panicking; public void Initialize(Transform player) {{ _player = player; _agent = GetComponent(); }} private void Update() {{ if (_agent == null) return; _repath -= Time.deltaTime; if (_repath > 0f) return; _repath = Random.Range(2f, 5f); if (_panicking) Flee(); else Wander(); if (Vector3.Distance(transform.position, _player.position) < 4f && Random.value < 0.2f) ReactToPlayer(); }} private void Wander() {{ _agent.speed = 1.4f; Vector3 rnd = transform.position + Random.insideUnitSphere * 20f; rnd.y = transform.position.y; _agent.SetDestination(rnd); }} private void Flee() {{ _agent.speed = 5f; Vector3 away = (transform.position - _player.position).normalized * 30f; _agent.SetDestination(transform.position + away); }} public void Panic(Vector3 source) {{ _panicking = true; Invoke(nameof(Calm), 8f); }} private void Calm() => _panicking = false; private void ReactToPlayer() {{ // Look at player, play idle anim — handled by Animator hooks. transform.rotation = Quaternion.LookRotation((_player.position - transform.position).normalized); }} }} }} ''') # =========================================================================== # 24. Economy system # =========================================================================== @register_tool class CreateEconomySystemTool(BaseUnityTool): """Generate an economy system with jobs, stores, bank and laundering.""" name = "create_economy_system" description = ( "Generate an economy system with jobs, stores, purchases, bank and " "money laundering." ) input_schema = { "type": "object", "properties": { "class_name": {"type": "string", "default": "EconomySystem"}, }, } def run(self, class_name: str = "EconomySystem", **_: Any) -> str: return self._wrap(f''' using System; using System.Collections.Generic; using UnityEngine; namespace OpenWorld.Economy {{ /// /// Central money & market service. Tracks cash and bank balance, /// stores with item prices, jobs that pay out on completion, and a /// laundering flow that converts dirty money (from crimes) to clean /// cash at a fee over time. /// public class {class_name} : MonoBehaviour {{ public static {class_name} Instance {{ get; private set; }} [Serializable] public class StoreItem {{ public string id; public string displayName; public int price; public bool legal = true; }} [Serializable] public class Store {{ public string id; public string name; public List items = new List(); }} [Serializable] public class Job {{ public string id; public string title; public int basePay; public float cooldown; }} [Header("Balances")] [SerializeField] private int cash = 500; [SerializeField] private int bank = 0; [SerializeField] private int dirtyMoney = 0; [SerializeField] private float launderFee = 0.3f; [SerializeField] private float launderRatePerSecond = 50f; [Header("Catalog")] [SerializeField] private List stores = new List(); [SerializeField] private List jobs = new List(); public int Cash => cash; public int Bank => bank; public int Dirty => dirtyMoney; public event Action OnBalanceChanged; // cash, bank, dirty private void Awake() {{ Instance = this; }} public void AddCash(int amount) {{ cash += amount; OnBalanceChanged?.Invoke(cash, bank, dirtyMoney); }} public void AddDirty(int amount) {{ dirtyMoney += amount; OnBalanceChanged?.Invoke(cash, bank, dirtyMoney); }} public bool Purchase(string storeId, string itemId) {{ Store s = stores.Find(x => x.id == storeId); if (s == null) return false; StoreItem item = s.items.Find(i => i.id == itemId); if (item == null || cash < item.price) return false; cash -= item.price; OnBalanceChanged?.Invoke(cash, bank, dirtyMoney); return true; }} public void Deposit(int amount) {{ amount = Mathf.Min(amount, cash); cash -= amount; bank += amount; OnBalanceChanged?.Invoke(cash, bank, dirtyMoney); }} public void Withdraw(int amount) {{ amount = Mathf.Min(amount, bank); bank -= amount; cash += amount; OnBalanceChanged?.Invoke(cash, bank, dirtyMoney); }} public void Work(string jobId) {{ Job j = jobs.Find(x => x.id == jobId); if (j == null) return; AddCash(j.basePay); }} public void StartLaundering() => InvokeRepeating(nameof(LaunderTick), 1f, 1f); public void StopLaundering() => CancelInvoke(nameof(LaunderTick)); private void LaunderTick() {{ if (dirtyMoney <= 0) return; int amount = Mathf.Min(dirtyMoney, Mathf.RoundToInt(launderRatePerSecond)); dirtyMoney -= amount; cash += Mathf.RoundToInt(amount * (1f - launderFee)); OnBalanceChanged?.Invoke(cash, bank, dirtyMoney); }} }} }} ''') # =========================================================================== # 25. Phone system # =========================================================================== @register_tool class CreatePhoneSystemTool(BaseUnityTool): """Generate an in-game phone with apps (map, contacts, messages, etc.).""" name = "create_phone_system" description = ( "Generate an in-game phone with apps: map, contacts, messages, " "internet, camera." ) input_schema = { "type": "object", "properties": { "class_name": {"type": "string", "default": "PhoneSystem"}, }, } def run(self, class_name: str = "PhoneSystem", **_: Any) -> str: return self._wrap(f''' using System; using System.Collections.Generic; using UnityEngine; using UnityEngine.UI; namespace OpenWorld.UI {{ /// /// In-game smartphone. Toggle with P; navigate apps with on-screen /// buttons. Apps: Home, Map, Contacts, Messages, Internet, Camera. /// Each app is a panel toggled on/off. Phone open slows game time. /// public class {class_name} : MonoBehaviour {{ public enum App {{ Home, Map, Contacts, Messages, Internet, Camera }} [Header("Root")] [SerializeField] private GameObject phoneRoot; [SerializeField] private CanvasGroup canvasGroup; [SerializeField] private Button[] appButtons; [SerializeField] private GameObject[] appPanels; [SerializeField] private Button homeButton; [SerializeField] private Button closeButton; [Header("Camera App")] [SerializeField] private Camera photoCamera; [SerializeField] private RawImage photoPreview; private bool _open; private App _current = App.Home; public bool IsOpen => _open; public event Action OnAppOpened; private void Awake() {{ for (int i = 0; i < appButtons.Length; i++) {{ int idx = i; if (appButtons[i] != null) appButtons[i].onClick.AddListener(() => OpenApp((App)idx)); }} if (homeButton != null) homeButton.onClick.AddListener(() => OpenApp(App.Home)); if (closeButton != null) closeButton.onClick.AddListener(Close); Close(); }} private void Update() {{ if (Input.GetKeyDown(KeyCode.P)) Toggle(); }} public void Toggle() {{ if (_open) Close(); else Open(); }} public void Open() {{ _open = true; if (phoneRoot != null) phoneRoot.SetActive(true); Time.timeScale = 0.3f; OpenApp(App.Home); }} public void Close() {{ _open = false; if (phoneRoot != null) phoneRoot.SetActive(false); Time.timeScale = 1f; }} public void OpenApp(App app) {{ _current = app; for (int i = 0; i < appPanels.Length; i++) if (appPanels[i] != null) appPanels[i].SetActive(i == (int)app); if (app == App.Camera) ActivateCamera(); OnAppOpened?.Invoke(app); }} private void ActivateCamera() {{ if (photoCamera == null) return; photoCamera.gameObject.SetActive(true); if (Input.GetKeyDown(KeyCode.F) && photoPreview != null) {{ StartCoroutine(CapturePhoto()); }} }} private System.Collections.IEnumerator CapturePhoto() {{ yield return new WaitForEndOfFrame(); RenderTexture rt = photoCamera.targetTexture; if (rt == null) yield break; Texture2D tex = new Texture2D(rt.width, rt.height, TextureFormat.RGB24, false); RenderTexture.active = rt; tex.ReadPixels(new Rect(0, 0, rt.width, rt.height), 0, 0); tex.Apply(); photoPreview.texture = tex; }} }} }} ''') # =========================================================================== # 26. Radio system # =========================================================================== @register_tool class CreateRadioSystemTool(BaseUnityTool): """Generate a radio system with stations and switching.""" name = "create_radio_system" description = ( "Generate a radio system with multiple stations, song lists and " "station switching." ) input_schema = { "type": "object", "properties": { "class_name": {"type": "string", "default": "RadioSystem"}, }, } def run(self, class_name: str = "RadioSystem", **_: Any) -> str: return self._wrap(f''' using System; using System.Collections.Generic; using UnityEngine; namespace OpenWorld.Audio {{ /// /// In-vehicle radio. Holds a list of stations each with a song queue. /// Player can switch station (Q/E), skip song (N) and toggle power. /// Audio is routed through a single AudioSource whose clip is swapped /// on track change. /// public class {class_name} : MonoBehaviour {{ [Serializable] public class Station {{ public string name; public string dj; public AudioClip[] songs; [NonSerialized] public int index; }} [Header("Source")] [SerializeField] private AudioSource source; [SerializeField] private List stations = new List(); [SerializeField] private int currentIndex; [SerializeField] private bool powered = true; [SerializeField] private float volume = 0.7f; public Station Current => (currentIndex >= 0 && currentIndex < stations.Count) ? stations[currentIndex] : null; public int StationCount => stations.Count; public event Action OnStationChanged; public event Action OnSongChanged; private void Awake() {{ if (source == null) source = gameObject.AddComponent(); source.loop = false; source.volume = volume; source.spatialBlend = 0f; if (stations.Count > 0) PlayCurrent(); }} private void Update() {{ if (!powered || source == null) return; if (!source.isPlaying) NextSong(); if (Input.GetKeyDown(KeyCode.Q)) PreviousStation(); if (Input.GetKeyDown(KeyCode.E)) NextStation(); if (Input.GetKeyDown(KeyCode.N)) NextSong(); if (Input.GetKeyDown(KeyCode.M)) TogglePower(); }} public void NextStation() {{ if (stations.Count == 0) return; currentIndex = (currentIndex + 1) % stations.Count; PlayCurrent(); }} public void PreviousStation() {{ if (stations.Count == 0) return; currentIndex = (currentIndex - 1 + stations.Count) % stations.Count; PlayCurrent(); }} public void NextSong() {{ var s = Current; if (s == null || s.songs.Length == 0) return; s.index = (s.index + 1) % s.songs.Length; source.clip = s.songs[s.index]; source.Play(); OnSongChanged?.Invoke(s, s.songs[s.index]); }} private void PlayCurrent() {{ var s = Current; if (s == null) return; source.clip = s.songs[s.index]; source.Play(); OnStationChanged?.Invoke(s); }} public void TogglePower() {{ powered = !powered; if (source != null) {{ if (powered) source.Play(); else source.Stop(); }} }} public void SetVolume(float v) {{ volume = Mathf.Clamp01(v); if (source != null) source.volume = volume; }} }} }} ''') # =========================================================================== # 27. Weapon wheel # =========================================================================== @register_tool class CreateWeaponWheelTool(BaseUnityTool): """Generate a radial weapon wheel with slow-mo selection.""" name = "create_weapon_wheel" description = ( "Generate a radial weapon wheel selector with slow-motion during " "selection." ) input_schema = { "type": "object", "properties": { "class_name": {"type": "string", "default": "WeaponWheel"}, }, } def run(self, class_name: str = "WeaponWheel", **_: Any) -> str: return self._wrap(f''' using System; using UnityEngine; using UnityEngine.UI; namespace OpenWorld.UI {{ /// /// Radial weapon selector. While held (Tab/Q), slows time to 0.2x, /// shows 8 weapon slots arranged in a circle, highlights the one /// under the mouse, and on release equips it via a callback. /// public class {class_name} : MonoBehaviour {{ [Serializable] public class WheelSlot {{ public string id; public Sprite icon; public Color tint = Color.white; }} [Header("Visual")] [SerializeField] private GameObject root; [SerializeField] private Image[] slotIcons; [SerializeField] private Image highlight; [SerializeField] private Transform wheelCenter; [SerializeField] private float radius = 120f; [Header("Slow-Mo")] [SerializeField] private float slowScale = 0.2f; [SerializeField] private float transitionSpeed = 8f; [SerializeField] private KeyCode openKey = KeyCode.Tab; [Header("Data")] [SerializeField] private WheelSlot[] slots = new WheelSlot[8]; private bool _open; private int _hovered = -1; private float _targetScale = 1f; public event Action OnSlotSelected; private void Awake() {{ if (root != null) root.SetActive(false); RenderSlots(); }} private void Update() {{ HandleOpen(); if (!_open) return; HandleHover(); Time.timeScale = Mathf.Lerp(Time.timeScale, _targetScale, transitionSpeed * Time.unscaledDeltaTime); if (Input.GetKeyUp(openKey)) Confirm(); }} private void HandleOpen() {{ bool held = Input.GetKey(openKey); if (held && !_open) {{ _open = true; _targetScale = slowScale; if (root != null) root.SetActive(true); }} if (!held && _open && Time.timeScale < 0.5f) {{ _targetScale = 1f; }} }} private void HandleHover() {{ if (wheelCenter == null) return; Vector2 mouse = Input.mousePosition; Vector2 center = RectTransformUtility.WorldToScreenPoint(null, wheelCenter.position); Vector2 dir = mouse - center; if (dir.magnitude > radius * 1.5f) {{ _hovered = -1; return; }} float angle = Mathf.Atan2(dir.y, dir.x) * Mathf.Rad2Deg; if (angle < 0f) angle += 360f; _hovered = Mathf.RoundToInt(angle / (360f / slots.Length)) % slots.Length; if (highlight != null && _hovered >= 0 && slotIcons[_hovered] != null) highlight.transform.position = slotIcons[_hovered].transform.position; }} private void Confirm() {{ _open = false; _targetScale = 1f; if (root != null) root.SetActive(false); if (_hovered >= 0) OnSlotSelected?.Invoke(_hovered); }} private void RenderSlots() {{ for (int i = 0; i < slotIcons.Length && i < slots.Length; i++) {{ float angle = i * (360f / slots.Length) * Mathf.Deg2Rad; Vector2 pos = new Vector2(Mathf.Cos(angle), Mathf.Sin(angle)) * radius; if (slotIcons[i] != null) {{ slotIcons[i].rectTransform.anchoredPosition = pos; slotIcons[i].sprite = slots[i].icon; slotIcons[i].color = slots[i].tint; }} }} }} public void SetSlot(int index, WheelSlot slot) {{ if (index < 0 || index >= slots.Length) return; slots[index] = slot; RenderSlots(); }} }} }} ''') # =========================================================================== # 28. Minimap system # =========================================================================== @register_tool class CreateMinimapSystemTool(BaseUnityTool): """Generate a rotating minimap with player, NPCs, missions and police.""" name = "create_minimap_system" description = ( "Generate a rotating minimap with player, NPCs, missions and police " "markers." ) input_schema = { "type": "object", "properties": { "class_name": {"type": "string", "default": "MinimapSystem"}, }, } def run(self, class_name: str = "MinimapSystem", **_: Any) -> str: return self._wrap(f''' using System.Collections.Generic; using UnityEngine; using UnityEngine.UI; namespace OpenWorld.UI {{ /// /// Top-down minimap. A dedicated orthographic camera follows the /// player and rotates with heading. Markers (NPC, police, mission, /// vehicle) are world-space sprites projected into screen space and /// clamped to the minimap's circular mask. /// public class {class_name} : MonoBehaviour {{ public enum MarkerType {{ Player, Npc, Police, Mission, Vehicle, Pickup }} [System.Serializable] public class Marker {{ public Transform target; public MarkerType type; public Sprite icon; }} [Header("Camera")] [SerializeField] private Camera minimapCamera; [SerializeField] private Transform player; [SerializeField] private float cameraHeight = 100f; [SerializeField] private float zoom = 60f; [Header("UI")] [SerializeField] private RectTransform minimapRect; [SerializeField] private RectTransform markerContainer; [SerializeField] private GameObject markerPrefab; [SerializeField] private float mapRadius = 90f; [Header("Markers")] [SerializeField] private List markers = new List(); [SerializeField] private Sprite policeIcon, missionIcon, npcIcon, vehicleIcon, pickupIcon; private readonly Dictionary _instances = new Dictionary(); private void LateUpdate() {{ if (player == null || minimapCamera == null) return; FollowPlayer(); UpdateMarkers(); }} private void FollowPlayer() {{ Vector3 pos = player.position; minimapCamera.transform.position = new Vector3(pos.x, cameraHeight, pos.z); minimapCamera.transform.rotation = Quaternion.Euler(90f, -player.eulerAngles.y, 0f); minimapCamera.orthographicSize = zoom; }} private void UpdateMarkers() {{ foreach (var kvp in _instances) if (kvp.Value != null) kvp.Value.SetActive(false); foreach (var m in markers) {{ if (m.target == null) continue; GameObject go = GetOrCreate(m); Vector3 local = minimapCamera.transform.InverseTransformPoint(m.target.position); Vector2 uiPos = new Vector2(local.x, local.z) / (zoom * 2f) * mapRadius * 2f; if (uiPos.magnitude > mapRadius) uiPos = uiPos.normalized * mapRadius; go.GetComponent().anchoredPosition = uiPos; go.SetActive(true); }} }} private GameObject GetOrCreate(Marker m) {{ if (_instances.TryGetValue(m.target, out GameObject go) && go != null) return go; go = Instantiate(markerPrefab, markerContainer); var img = go.GetComponent(); if (img != null) img.sprite = m.icon ?? m.type switch {{ MarkerType.Police => policeIcon, MarkerType.Mission => missionIcon, MarkerType.Npc => npcIcon, MarkerType.Vehicle => vehicleIcon, MarkerType.Pickup => pickupIcon, _ => null, }}; _instances[m.target] = go; return go; }} public void RegisterMarker(Marker m) {{ if (!markers.Contains(m)) markers.Add(m); }} public void UnregisterMarker(Transform t) {{ markers.RemoveAll(m => m.target == t); if (_instances.TryGetValue(t, out GameObject go) && go != null) Destroy(go); _instances.Remove(t); }} }} }} ''') # =========================================================================== # 29. Explosion system # =========================================================================== @register_tool class CreateExplosionSystemTool(BaseUnityTool): """Generate an explosion system with chain reactions and debris.""" name = "create_explosion_system" description = ( "Generate an explosion system with chain reactions, vehicle " "explosions, building damage and debris." ) input_schema = { "type": "object", "properties": { "class_name": {"type": "string", "default": "ExplosionSystem"}, }, } def run(self, class_name: str = "ExplosionSystem", **_: Any) -> str: return self._wrap(f''' using System.Collections; using System.Collections.Generic; using UnityEngine; namespace OpenWorld.FX {{ /// /// Manages explosion events. Applies radial damage & physics impulse, /// spawns fire/smoke/debris, and propagates to nearby explosive /// barrels/vehicles for chain reactions. /// public class {class_name} : MonoBehaviour {{ public static {class_name} Instance {{ get; private set; }} [Header("Damage")] [SerializeField] private float baseDamage = 100f; [SerializeField] private float baseRadius = 10f; [SerializeField] private float baseForce = 1200f; [Header("Effects")] [SerializeField] private GameObject explosionVfx; [SerializeField] private GameObject fireVfx; [SerializeField] private GameObject smokeVfx; [SerializeField] private GameObject debrisPrefab; [SerializeField] private int debrisCount = 12; [SerializeField] private AudioClip explosionSound; [SerializeField] private AudioSource audioSource; [Header("Chain Reaction")] [SerializeField] private LayerMask explosiveMask; [SerializeField] private float chainDelay = 0.15f; private void Awake() {{ Instance = this; }} public void Explode(Vector3 center, float damageMult = 1f, float radiusMult = 1f) {{ float dmg = baseDamage * damageMult; float radius = baseRadius * radiusMult; SpawnEffects(center, radius); PlaySound(center); ApplyDamageAndForce(center, dmg, radius); SpawnDebris(center, radius); PropagateChain(center, radius, damageMult, radiusMult); }} private void SpawnEffects(Vector3 center, float radius) {{ if (explosionVfx != null) Instantiate(explosionVfx, center, Quaternion.identity); if (fireVfx != null) {{ GameObject fire = Instantiate(fireVfx, center, Quaternion.identity); Destroy(fire, 6f); }} if (smokeVfx != null) {{ GameObject smoke = Instantiate(smokeVfx, center, Quaternion.identity); Destroy(smoke, 10f); }} }} private void PlaySound(Vector3 center) {{ if (audioSource != null && explosionSound != null) audioSource.PlayOneShot(explosionSound); }} private void ApplyDamageAndForce(Vector3 center, float dmg, float radius) {{ Collider[] hits = Physics.OverlapSphere(center, radius); foreach (var h in hits) {{ float falloff = 1f - Mathf.Clamp01(Vector3.Distance(center, h.transform.position) / radius); var dmgable = h.GetComponent(); if (dmgable != null) dmgable.TakeDamage(dmg * falloff, center); if (h.TryGetComponent(out var rb)) rb.AddExplosionForce(baseForce * falloff, center, radius, 0.5f, ForceMode.Impulse); var vehicle = h.GetComponent(); if (vehicle != null) vehicle.Damage(dmg * falloff); }} }} private void SpawnDebris(Vector3 center, float radius) {{ if (debrisPrefab == null) return; for (int i = 0; i < debrisCount; i++) {{ Vector3 offset = Random.onUnitSphere * radius * 0.5f; offset.y = Mathf.Abs(offset.y); GameObject debris = Instantiate(debrisPrefab, center + offset, Random.rotation); if (debris.TryGetComponent(out var rb)) rb.AddForce(offset.normalized * 8f, ForceMode.Impulse); Destroy(debris, 8f); }} }} private void PropagateChain(Vector3 center, float radius, float dmgMult, float radMult) {{ Collider[] hits = Physics.OverlapSphere(center, radius, explosiveMask); foreach (var h in hits) {{ var explodable = h.GetComponent(); if (explodable == null) continue; StartCoroutine(ChainExplode(explodable.transform.position, dmgMult * 0.8f, radMult * 0.8f)); }} }} private IEnumerator ChainExplode(Vector3 pos, float dmgMult, float radMult) {{ yield return new WaitForSeconds(chainDelay + Random.value * 0.1f); Explode(pos, dmgMult, radMult); }} }} public class ExplodableVehicle : MonoBehaviour {{ [SerializeField] private float health = 100f; public void Damage(float amount) {{ health -= amount; if (health <= 0f) ExplosionSystem.Instance.Explode(transform.position, 1.5f, 1.3f); }} }} }} ''') # =========================================================================== # 30. Ragdoll system # =========================================================================== @register_tool class CreateRagdollSystemTool(BaseUnityTool): """Generate a ragdoll system with death physics and impact reactions.""" name = "create_ragdoll_system" description = ( "Generate a ragdoll system with death physics, body piles and " "impact reactions." ) input_schema = { "type": "object", "properties": { "class_name": {"type": "string", "default": "RagdollSystem"}, }, } def run(self, class_name: str = "RagdollSystem", **_: Any) -> str: return self._wrap(f''' using System.Collections.Generic; using UnityEngine; namespace OpenWorld.AI {{ /// /// Toggles a humanoid ragdoll. When active, Animator is disabled and /// per-bone Rigidbodies/Colliders are enabled; forces can be applied /// for impact reactions. Dead bodies persist so piles form naturally. /// [RequireComponent(typeof(Animator))] public class {class_name} : MonoBehaviour {{ [Header("Bones")] [SerializeField] private Rigidbody[] ragdollBodies; [SerializeField] private Collider[] ragdollColliders; [SerializeField] private Transform hipBone; [Header("Reaction")] [SerializeField] private float impactForce = 6f; [SerializeField] private float settleThreshold = 0.3f; [SerializeField] private float settleTime = 2f; private Animator _animator; private bool _active; private float _settleTimer; public bool IsActive => _active; private void Awake() {{ _animator = GetComponent(); SetRagdoll(false); }} public void Activate(Vector3 impulseSource, float force = 1f) {{ if (_active) return; _active = true; if (_animator != null) _animator.enabled = false; SetRagdoll(true); ApplyImpulse(impulseSource, force); }} public void Deactivate() {{ _active = false; if (_animator != null) _animator.enabled = true; SetRagdoll(false); }} private void SetRagdoll(bool enabled) {{ foreach (var rb in ragdollBodies) if (rb != null) rb.isKinematic = !enabled; foreach (var col in ragdollColliders) if (col != null) col.enabled = enabled; }} private void ApplyImpulse(Vector3 source, float mult) {{ if (hipBone == null) return; Vector3 dir = (hipBone.position - source).normalized + Vector3.up * 0.5f; foreach (var rb in ragdollBodies) if (rb != null) rb.AddForce(dir * impactForce * mult, ForceMode.Impulse); }} private void Update() {{ if (!_active) return; float maxVel = 0f; foreach (var rb in ragdollBodies) if (rb != null) maxVel = Mathf.Max(maxVel, rb.velocity.magnitude); if (maxVel < settleThreshold) {{ _settleTimer += Time.deltaTime; if (_settleTimer >= settleTime) Settle(); }} else _settleTimer = 0f; }} private void Settle() {{ // Optionally convert ragdoll into a static corpse mesh for perf. foreach (var rb in ragdollBodies) if (rb != null) rb.isKinematic = true; }} }} }} ''') # =========================================================================== # 31. Cloth physics # =========================================================================== @register_tool class CreateClothPhysicsTool(BaseUnityTool): """Generate a cloth physics system for flags, curtains and clothing.""" name = "create_cloth_physics" description = ( "Generate a cloth physics system for flags, curtains and clothing " "simulation." ) input_schema = { "type": "object", "properties": { "class_name": {"type": "string", "default": "ClothPhysicsSystem"}, }, } def run(self, class_name: str = "ClothPhysicsSystem", **_: Any) -> str: return self._wrap(f''' using System.Collections.Generic; using UnityEngine; namespace OpenWorld.FX {{ /// /// Wraps Unity's Cloth component for interactive cloth (flags, curtains, /// character capes). Provides wind control, preset attachment weights /// and runtime toggling. Multiple cloth objects can be registered. /// public class {class_name} : MonoBehaviour {{ [System.Serializable] public class ClothInstance {{ public string id; public Cloth cloth; public float windFactor = 1f; }} [Header("Global Wind")] [SerializeField] private Vector3 windDirection = new Vector3(1f, 0f, 0.2f); [SerializeField] private float windStrength = 2f; [SerializeField] private float gustFrequency = 0.3f; [SerializeField] private float gustAmplitude = 1.5f; [Header("Instances")] [SerializeField] private List cloths = new List(); public void Register(ClothInstance ci) {{ if (!cloths.Contains(ci)) cloths.Add(ci); }} public void Unregister(string id) => cloths.RemoveAll(c => c.id == id); private void Update() {{ float gust = 1f + Mathf.Sin(Time.time * gustFrequency * Mathf.PI * 2f) * gustAmplitude; Vector3 wind = windDirection.normalized * windStrength * gust; foreach (var ci in cloths) {{ if (ci.cloth == null) continue; ci.cloth.externalAcceleration = wind * ci.windFactor; ci.cloth.randomAcceleration = wind * 0.2f * ci.windFactor; }} }} public void AttachTopRow(string id) {{ var ci = cloths.Find(c => c.id == id); if (ci?.cloth == null) return; var coefficients = ci.cloth.coefficients; int vertsPerRow = Mathf.CeilToInt(Mathf.Sqrt(coefficients.Length)); for (int i = 0; i < coefficients.Length; i++) {{ int row = i / vertsPerRow; coefficients[i].maxDistance = row == 0 ? 0f : float.PositiveInfinity; }} ci.cloth.coefficients = coefficients; }} }} }} ''') # =========================================================================== # 32. Weather system # =========================================================================== @register_tool class CreateWeatherSystemTool(BaseUnityTool): """Generate a weather system with rain, fog, clear and storm states.""" name = "create_weather_system" description = ( "Generate a weather system: rain, fog, clear, storm with particle " "effects and lighting changes." ) input_schema = { "type": "object", "properties": { "class_name": {"type": "string", "default": "WeatherSystem"}, }, } def run(self, class_name: str = "WeatherSystem", **_: Any) -> str: return self._wrap(f''' using System; using UnityEngine; namespace OpenWorld.World {{ /// /// State-machine weather controller. Cycles between Clear, Cloudy, /// Rain, Fog and Storm. Each state adjusts ambient light, fog density, /// particle systems (rain/snow), audio bed and wind. Transitions are /// smoothed over a few seconds. /// public class {class_name} : MonoBehaviour {{ public enum Weather {{ Clear, Cloudy, Rain, Fog, Storm }} [Serializable] public struct WeatherPreset {{ public Weather type; public Color ambientColor; public float fogDensity; public Color fogColor; public float lightIntensity; public float windStrength; public ParticleSystem particles; public AudioClip ambientSound; }} [Header("State")] [SerializeField] private Weather current = Weather.Clear; [SerializeField] private WeatherPreset[] presets; [SerializeField] private float transitionTime = 8f; [SerializeField] private float changeInterval = 120f; [Header("Refs")] [SerializeField] private Light sun; [SerializeField] private AudioSource ambientSource; [SerializeField] private WindZone windZone; private WeatherPreset _target; private WeatherPreset _current; private float _transitionProgress = 1f; private float _changeTimer; public event Action OnWeatherChanged; private void Awake() {{ _current = GetPreset(current); _target = _current; ApplyImmediate(_current); }} private void Update() {{ HandleAutoChange(); HandleTransition(); }} private void HandleAutoChange() {{ _changeTimer += Time.deltaTime; if (_changeTimer < changeInterval) return; _changeTimer = 0f; Weather[] values = (Weather[])Enum.GetValues(typeof(Weather)); Weather next; do {{ next = values[UnityEngine.Random.Range(0, values.Length)]; }} while (next == current); SetWeather(next); }} public void SetWeather(Weather w) {{ current = w; _target = GetPreset(w); _transitionProgress = 0f; OnWeatherChanged?.Invoke(w); if (ambientSource != null && _target.ambientSound != null) {{ ambientSource.clip = _target.ambientSound; ambientSource.Play(); }} if (_target.particles != null) _target.particles.Play(); }} private void HandleTransition() {{ if (_transitionProgress >= 1f) return; _transitionProgress = Mathf.Min(1f, _transitionProgress + Time.deltaTime / transitionTime); float t = _transitionProgress; RenderSettings.ambientLight = Color.Lerp(_current.ambientColor, _target.ambientColor, t); RenderSettings.fogColor = Color.Lerp(_current.fogColor, _target.fogColor, t); RenderSettings.fogDensity = Mathf.Lerp(_current.fogDensity, _target.fogDensity, t); if (sun != null) sun.intensity = Mathf.Lerp(_current.lightIntensity, _target.lightIntensity, t); if (windZone != null) windZone.windMain = Mathf.Lerp(_current.windStrength, _target.windStrength, t); if (_transitionProgress >= 1f) {{ _current = _target; if (_current.particles != null) _current.particles.Stop(); }} }} private void ApplyImmediate(WeatherPreset p) {{ RenderSettings.ambientLight = p.ambientColor; RenderSettings.fogColor = p.fogColor; RenderSettings.fogDensity = p.fogDensity; RenderSettings.fog = p.fogDensity > 0.001f; if (sun != null) sun.intensity = p.lightIntensity; if (windZone != null) windZone.windMain = p.windStrength; }} private WeatherPreset GetPreset(Weather w) {{ foreach (var p in presets) if (p.type == w) return p; return default; }} }} }} ''') # =========================================================================== # 33. CCTV system # =========================================================================== @register_tool class CreateCctvSystemTool(BaseUnityTool): """Generate a CCTV security camera system with detection and alarms.""" name = "create_cctv_system" description = ( "Generate a CCTV security camera system with detection, alarm " "triggering and recording hooks." ) input_schema = { "type": "object", "properties": { "class_name": {"type": "string", "default": "CctvSystem"}, }, } def run(self, class_name: str = "CctvSystem", **_: Any) -> str: return self._wrap(f''' using System.Collections.Generic; using UnityEngine; namespace OpenWorld.Security {{ /// /// Security camera. Sweeps a cone via FieldOfView raycasts; on /// detecting the player (or a tagged target) it raises an alarm event /// and starts recording (rendered to a RenderTexture). Guards can be /// alerted via the OnDetected event. /// public class {class_name} : MonoBehaviour {{ [Header("Detection")] [SerializeField] private Transform pivot; [SerializeField] private float viewDistance = 18f; [SerializeField] private float viewAngle = 45f; [SerializeField] private float sweepSpeed = 30f; [SerializeField] private float sweepRange = 90f; [SerializeField] private LayerMask targetMask; [SerializeField] private LayerMask obstructionMask; [SerializeField] private Transform alarmTarget; [Header("Visual")] [SerializeField] private Light indicator; [SerializeField] private Color idleColor = Color.green; [SerializeField] private Color alertColor = Color.red; [SerializeField] private Camera renderCamera; [SerializeField] private RenderTexture outputTexture; public bool Alerted {{ get; private set; }} public static System.Action OnGlobalDetection; private float _baseYaw; private float _sweepT; private void Awake() {{ if (pivot != null) _baseYaw = pivot.eulerAngles.y; if (renderCamera != null) renderCamera.targetTexture = outputTexture; }} private void Update() {{ Sweep(); Detect(); }} private void Sweep() {{ if (Alerted || pivot == null) return; _sweepT += Time.deltaTime * sweepSpeed; float yaw = _baseYaw + Mathf.Sin(_sweepT * Mathf.Deg2Rad) * sweepRange * 0.5f; pivot.rotation = Quaternion.Euler(0f, yaw, 0f); }} private void Detect() {{ if (pivot == null) return; Collider[] candidates = Physics.OverlapSphere(pivot.position, viewDistance, targetMask); foreach (var c in candidates) {{ Vector3 dir = (c.transform.position - pivot.position).normalized; if (Vector3.Angle(pivot.forward, dir) > viewAngle * 0.5f) continue; if (Physics.Raycast(pivot.position, dir, out RaycastHit hit, viewDistance, obstructionMask)) if (hit.transform != c.transform) continue; RaiseAlarm(c.transform); return; }} }} private void RaiseAlarm(Transform target) {{ Alerted = true; if (indicator != null) indicator.color = alertColor; OnGlobalDetection?.Invoke(target); WantedSystem.Instance?.ReportCrime(20f); if (renderCamera != null) renderCamera.gameObject.SetActive(true); }} public void Reset() {{ Alerted = false; if (indicator != null) indicator.color = idleColor; if (renderCamera != null) renderCamera.gameObject.SetActive(false); }} private void OnDrawGizmosSelected() {{ if (pivot == null) return; Gizmos.color = Alerted ? Color.red : Color.yellow; Gizmos.DrawWireSphere(pivot.position, viewDistance); Vector3 left = Quaternion.Euler(0, -viewAngle * 0.5f, 0) * pivot.forward * viewDistance; Vector3 right = Quaternion.Euler(0, viewAngle * 0.5f, 0) * pivot.forward * viewDistance; Gizmos.DrawLine(pivot.position, pivot.position + left); Gizmos.DrawLine(pivot.position, pivot.position + right); }} }} }} ''') # =========================================================================== # 34. Dialogue system # =========================================================================== @register_tool class CreateDialogueSystemTool(BaseUnityTool): """Generate a branching dialogue system with subtitles.""" name = "create_dialogue_system" description = ( "Generate a dialogue system with NPC conversations, branching " "dialogue and subtitles." ) input_schema = { "type": "object", "properties": { "class_name": {"type": "string", "default": "DialogueSystem"}, }, } def run(self, class_name: str = "DialogueSystem", **_: Any) -> str: return self._wrap(f''' using System; using System.Collections.Generic; using UnityEngine; using UnityEngine.UI; namespace OpenWorld.Dialogue {{ /// /// Branching dialogue engine. Conversations are directed graphs of /// nodes; each node has NPC lines and a list of player responses that /// link to other nodes. Subtitles render via a Text panel and a typewriter /// coroutine. Choices are shown as buttons. /// public class {class_name} : MonoBehaviour {{ [Serializable] public class DialogueNode {{ public string id; [TextArea] public string npcLine; public AudioClip voiceClip; public List choices = new List(); public string onEnterEvent; }} [Serializable] public class DialogueChoice {{ [TextArea] public string text; public string targetNodeId; public string requiredFlag; public string setFlag; }} [Serializable] public class Conversation {{ public string id; public List nodes = new List(); }} [Header("UI")] [SerializeField] private GameObject panel; [SerializeField] private Text npcNameText; [SerializeField] private Text subtitleText; [SerializeField] private Transform choiceContainer; [SerializeField] private Button choicePrefab; [SerializeField] private float typeSpeed = 40f; [Header("Audio")] [SerializeField] private AudioSource voiceSource; [Header("Data")] [SerializeField] private List conversations = new List(); private readonly HashSet _flags = new HashSet(); private Conversation _current; private DialogueNode _node; private bool _typing; public event Action OnEventTriggered; public bool IsActive => _current != null; public void StartConversation(string id, string npcName) {{ _current = conversations.Find(c => c.id == id); if (_current == null || _current.nodes.Count == 0) return; if (npcNameText != null) npcNameText.text = npcName; if (panel != null) panel.SetActive(true); Cursor.lockState = CursorLockMode.None; Cursor.visible = true; ShowNode(_current.nodes[0]); }} public void EndConversation() {{ _current = null; _node = null; if (panel != null) panel.SetActive(false); Cursor.lockState = CursorLockMode.Locked; Cursor.visible = false; }} private void ShowNode(DialogueNode node) {{ _node = node; if (!string.IsNullOrEmpty(node.onEnterEvent)) OnEventTriggered?.Invoke(node.onEnterEvent); if (node.voiceClip != null && voiceSource != null) voiceSource.PlayOneShot(node.voiceClip); StartCoroutine(TypeLine(node.npcLine)); RenderChoices(node); }} private System.Collections.IEnumerator TypeLine(string line) {{ _typing = true; if (subtitleText != null) subtitleText.text = ""; foreach (char c in line) {{ if (subtitleText != null) subtitleText.text += c; yield return new WaitForSeconds(1f / typeSpeed); }} _typing = false; }} private void RenderChoices(DialogueNode node) {{ if (choiceContainer == null || choicePrefab == null) return; foreach (Transform child in choiceContainer) Destroy(child.gameObject); foreach (var ch in node.choices) {{ if (!string.IsNullOrEmpty(ch.requiredFlag) && !_flags.Contains(ch.requiredFlag)) continue; Button btn = Instantiate(choicePrefab, choiceContainer); btn.GetComponentInChildren().text = ch.text; btn.onClick.AddListener(() => Choose(ch)); }} }} private void Choose(DialogueChoice choice) {{ if (!string.IsNullOrEmpty(choice.setFlag)) _flags.Add(choice.setFlag); if (string.IsNullOrEmpty(choice.targetNodeId)) {{ EndConversation(); return; }} var next = _current.nodes.Find(n => n.id == choice.targetNodeId); if (next != null) ShowNode(next); else EndConversation(); }} private void Update() {{ if (!IsActive) return; if (Input.GetKeyDown(KeyCode.Escape)) EndConversation(); if (_typing && Input.GetMouseButtonDown(0)) _typing = false; }} }} }} ''') # =========================================================================== # 35. Mission system # =========================================================================== @register_tool class CreateMissionSystemTool(BaseUnityTool): """Generate a mission system with story, side and random events.""" name = "create_mission_system" description = ( "Generate a mission system with story missions, side missions, " "random events and mission givers." ) input_schema = { "type": "object", "properties": { "class_name": {"type": "string", "default": "MissionSystem"}, }, } def run(self, class_name: str = "MissionSystem", **_: Any) -> str: return self._wrap(f''' using System; using System.Collections.Generic; using UnityEngine; namespace OpenWorld.Missions {{ /// /// High-level mission director. Story missions unlock linearly; side /// missions are available from givers; random events spawn opportunistically. /// Each mission has a starting trigger and a completion check. /// public class {class_name} : MonoBehaviour {{ public enum MissionKind {{ Story, Side, RandomEvent }} [Serializable] public class Mission {{ public string id; public string title; public MissionKind kind; public string giverId; public Vector3 startMarker; public string questId; // links to QuestSystem public int moneyReward; public int xpReward; public string unlocksMissionId; public bool available; public bool started; public bool completed; }} [Header("Missions")] [SerializeField] private List missions = new List(); [SerializeField] private Mission[] randomEventPool; [Header("Random Events")] [SerializeField] private float randomEventInterval = 180f; [SerializeField] private float randomEventChance = 0.5f; private float _randomTimer; private Mission _activeStory; public IReadOnlyList All => missions; public event Action OnMissionStarted; public event Action OnMissionCompleted; private void Start() {{ // Unlock the first story mission. foreach (var m in missions) if (m.kind == MissionKind.Story && !m.completed) {{ m.available = true; _activeStory = m; break; }} _randomTimer = randomEventInterval; }} private void Update() {{ HandleRandomEvents(); CheckCompletion(); }} public void StartMission(string id) {{ Mission m = missions.Find(x => x.id == id); if (m == null || !m.available || m.started) return; m.started = true; QuestSystem.Instance?.Accept(m.questId); OnMissionStarted?.Invoke(m); }} private void CheckCompletion() {{ foreach (var m in missions) {{ if (!m.started || m.completed) continue; if (QuestSystem.Instance == null) continue; var q = QuestSystem.Instance.ActiveQuests; bool done = false; foreach (var qq in q) if (qq.id == m.questId && qq.completed) done = true; if (done) CompleteMission(m); }} }} private void CompleteMission(Mission m) {{ m.completed = true; EconomySystem.Instance?.AddCash(m.moneyReward); OnMissionCompleted?.Invoke(m); if (!string.IsNullOrEmpty(m.unlocksMissionId)) {{ Mission next = missions.Find(x => x.id == m.unlocksMissionId); if (next != null) next.available = true; }} }} private void HandleRandomEvents() {{ _randomTimer -= Time.deltaTime; if (_randomTimer > 0f) return; _randomTimer = randomEventInterval; if (randomEventPool == null || randomEventPool.Length == 0) return; if (UnityEngine.Random.value > randomEventChance) return; Mission re = randomEventPool[UnityEngine.Random.Range(0, randomEventPool.Length)]; Mission copy = new Mission {{ id = re.id + "_" + Time.frameCount, title = re.title, kind = MissionKind.RandomEvent, giverId = re.giverId, startMarker = re.startMarker, questId = re.questId, moneyReward = re.moneyReward, xpReward = re.xpReward, available = true }}; missions.Add(copy); StartMission(copy.id); }} }} }} ''') # =========================================================================== # 36. Garage system # =========================================================================== @register_tool class CreateGarageSystemTool(BaseUnityTool): """Generate a garage system for vehicle storage and customization.""" name = "create_garage_system" description = ( "Generate a garage system with vehicle storage, customization, " "painting and repairs." ) input_schema = { "type": "object", "properties": { "class_name": {"type": "string", "default": "GarageSystem"}, }, } def run(self, class_name: str = "GarageSystem", **_: Any) -> str: return self._wrap(f''' using System; using System.Collections.Generic; using UnityEngine; namespace OpenWorld.Vehicles {{ /// /// Garage service. Stores up to N vehicles per garage, restores health /// on storage, and exposes customization actions: repaint, repair, /// upgrade engine/armor. /// public class {class_name} : MonoBehaviour {{ [Serializable] public class StoredVehicle {{ public string modelId; public float health; public Color paint = Color.white; public int engineLevel = 1; public int armorLevel = 1; public int tireLevel = 1; }} [Header("Capacity")] [SerializeField] private int capacity = 10; [SerializeField] private Transform spawnPoint; [SerializeField] private List stored = new List(); [SerializeField] private GameObject[] vehiclePrefabs; [Header("Costs")] [SerializeField] private int repairCost = 500; [SerializeField] private int repaintCost = 200; [SerializeField] private int upgradeCost = 2000; public IReadOnlyList Stored => stored; public bool Store(GameObject vehicle, string modelId) {{ if (stored.Count >= capacity) return false; var sv = new StoredVehicle {{ modelId = modelId, health = 100f, paint = vehicle.GetComponent()?.material.color ?? Color.white }}; stored.Add(sv); Destroy(vehicle); return true; }} public GameObject Retrieve(int index) {{ if (index < 0 || index >= stored.Count || spawnPoint == null) return null; StoredVehicle sv = stored[index]; GameObject prefab = System.Array.Find(vehiclePrefabs, p => p.name == sv.modelId); if (prefab == null) return null; GameObject go = Instantiate(prefab, spawnPoint.position, spawnPoint.rotation); if (go.TryGetComponent(out var r)) r.material.color = sv.paint; stored.RemoveAt(index); return go; }} public bool Repair(int index) {{ if (!Charge(repairCost)) return false; if (index < 0 || index >= stored.Count) return false; stored[index].health = 100f; return true; }} public bool Repaint(int index, Color color) {{ if (!Charge(repaintCost)) return false; if (index < 0 || index >= stored.Count) return false; stored[index].paint = color; return true; }} public bool UpgradeEngine(int index) {{ if (!Charge(upgradeCost)) return false; stored[index].engineLevel = Mathf.Min(5, stored[index].engineLevel + 1); return true; }} public bool UpgradeArmor(int index) {{ if (!Charge(upgradeCost)) return false; stored[index].armorLevel = Mathf.Min(5, stored[index].armorLevel + 1); return true; }} private bool Charge(int amount) {{ var econ = EconomySystem.Instance; if (econ == null || econ.Cash < amount) return false; econ.AddCash(-amount); return true; }} }} }} ''') # =========================================================================== # 37. Property system # =========================================================================== @register_tool class CreatePropertySystemTool(BaseUnityTool): """Generate a property system for buying, selling and income.""" name = "create_property_system" description = ( "Generate a property system: buy/sell properties, income " "generation, safe houses." ) input_schema = { "type": "object", "properties": { "class_name": {"type": "string", "default": "PropertySystem"}, }, } def run(self, class_name: str = "PropertySystem", **_: Any) -> str: return self._wrap(f''' using System; using System.Collections.Generic; using UnityEngine; namespace OpenWorld.Economy {{ /// /// Real-estate manager. Each property has a price, weekly income, and /// may serve as a safe house (heal/save point). Income is paid out on /// a configurable interval. /// public class {class_name} : MonoBehaviour {{ public static {class_name} Instance {{ get; private set; }} [Serializable] public class Property {{ public string id; public string displayName; public int price; public int weeklyIncome; public bool safeHouse; public Vector3 location; [NonSerialized] public bool owned; }} [Header("Catalog")] [SerializeField] private List properties = new List(); [Header("Income")] [SerializeField] private float payoutIntervalMinutes = 5f; private float _payoutTimer; public IReadOnlyList All => properties; public event Action OnPurchased; public event Action OnSold; public event Action OnIncomePaid; private void Awake() {{ Instance = this; }} public bool Buy(string id) {{ Property p = properties.Find(x => x.id == id); if (p == null || p.owned) return false; var econ = EconomySystem.Instance; if (econ == null || econ.Cash < p.price) return false; econ.AddCash(-p.price); p.owned = true; OnPurchased?.Invoke(p); return true; }} public bool Sell(string id) {{ Property p = properties.Find(x => x.id == id); if (p == null || !p.owned) return false; EconomySystem.Instance?.AddCash(p.price / 2); p.owned = false; OnSold?.Invoke(p); return true; }} public bool UseSafeHouse(string id) {{ Property p = properties.Find(x => x.id == id); if (p == null || !p.owned || !p.safeHouse) return false; // Player health/save handled by listeners. return true; }} private void Update() {{ _payoutTimer += Time.deltaTime; float interval = payoutIntervalMinutes * 60f; if (_payoutTimer < interval) return; _payoutTimer = 0f; int total = 0; foreach (var p in properties) if (p.owned) total += p.weeklyIncome; if (total > 0) {{ EconomySystem.Instance?.AddCash(total); OnIncomePaid?.Invoke(total); }} }} }} }} ''') # =========================================================================== # 38. Character customization # =========================================================================== @register_tool class CreateCharacterCustomizationTool(BaseUnityTool): """Generate a character customization system (skin, hair, clothing).""" name = "create_character_customization" description = ( "Generate a character customization system: skin, hair, clothing, " "accessories." ) input_schema = { "type": "object", "properties": { "class_name": {"type": "string", "default": "CharacterCustomization"}, }, } def run(self, class_name: str = "CharacterCustomization", **_: Any) -> str: return self._wrap(f''' using System; using UnityEngine; namespace OpenWorld.Player {{ /// /// Character appearance manager. Blends skin tones via material color, /// swaps hair meshes from a list, and toggles clothing/attachment /// GameObjects. Saves an appearance string for persistence. /// public class {class_name} : MonoBehaviour {{ [Serializable] public struct HairOption {{ public string id; public GameObject mesh; }} [Serializable] public struct ClothingOption {{ public string id; public GameObject mesh; }} [Serializable] public struct AccessoryOption {{ public string id; public GameObject mesh; }} [Header("Body")] [SerializeField] private SkinnedMeshRenderer bodyMesh; [SerializeField] private Color[] skinTones; [SerializeField] private int skinIndex; [Header("Hair")] [SerializeField] private HairOption[] hairOptions; [SerializeField] private int hairIndex; [Header("Clothing")] [SerializeField] private ClothingOption[] tops; [SerializeField] private int topIndex; [SerializeField] private ClothingOption[] bottoms; [SerializeField] private int bottomIndex; [Header("Accessories")] [SerializeField] private AccessoryOption[] accessories; [SerializeField] private bool[] accessoryToggles; public int SkinIndex => skinIndex; public int HairIndex => hairIndex; public int TopIndex => topIndex; public int BottomIndex => bottomIndex; public void SetSkin(int index) {{ skinIndex = Mathf.Clamp(index, 0, skinTones.Length - 1); if (bodyMesh != null) bodyMesh.material.color = skinTones[skinIndex]; }} public void SetHair(int index) {{ hairIndex = Mathf.Clamp(index, 0, hairOptions.Length - 1); for (int i = 0; i < hairOptions.Length; i++) if (hairOptions[i].mesh != null) hairOptions[i].mesh.SetActive(i == hairIndex); }} public void SetTop(int index) {{ topIndex = Mathf.Clamp(index, 0, tops.Length - 1); for (int i = 0; i < tops.Length; i++) if (tops[i].mesh != null) tops[i].mesh.SetActive(i == topIndex); }} public void SetBottom(int index) {{ bottomIndex = Mathf.Clamp(index, 0, bottoms.Length - 1); for (int i = 0; i < bottoms.Length; i++) if (bottoms[i].mesh != null) bottoms[i].mesh.SetActive(i == bottomIndex); }} public void ToggleAccessory(int index) {{ if (index < 0 || index >= accessories.Length) return; accessoryToggles[index] = !accessoryToggles[index]; if (accessories[index].mesh != null) accessories[index].mesh.SetActive(accessoryToggles[index]); }} public string Serialize() {{ return $"{{skinIndex}}|{{hairIndex}}|{{topIndex}}|{{bottomIndex}}|{{string.Join(",", accessoryToggles)}}"; }} public void Deserialize(string data) {{ if (string.IsNullOrEmpty(data)) return; string[] parts = data.Split('|'); if (parts.Length < 4) return; if (int.TryParse(parts[0], out int s)) SetSkin(s); if (int.TryParse(parts[1], out int h)) SetHair(h); if (int.TryParse(parts[2], out int t)) SetTop(t); if (int.TryParse(parts[3], out int b)) SetBottom(b); }} }} }} ''') # =========================================================================== # 39. Cutscene system # =========================================================================== @register_tool class CreateCutsceneSystemTool(BaseUnityTool): """Generate a cutscene system with camera sequences and letterbox.""" name = "create_cutscene_system" description = ( "Generate a cutscene system with camera sequences, character " "animation and letterbox." ) input_schema = { "type": "object", "properties": { "class_name": {"type": "string", "default": "CutsceneSystem"}, }, } def run(self, class_name: str = "CutsceneSystem", **_: Any) -> str: return self._wrap(f''' using System; using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.UI; namespace OpenWorld.Cinematics {{ /// /// Sequencer-based cutscene player. Each shot has a camera, duration, /// look-at target and dialogue line. A black letterbox bar animates in /// at the start. Player input is disabled during playback. /// public class {class_name} : MonoBehaviour {{ [Serializable] public class Shot {{ public Camera camera; public Transform lookAt; public float duration = 4f; [TextArea] public string subtitle; public AnimationClip actorAnimation; public Animator actorAnimator; }} [Serializable] public class Cutscene {{ public string id; public List shots = new List(); }} [Header("Letterbox")] [SerializeField] private RectTransform topBar; [SerializeField] private RectTransform bottomBar; [SerializeField] private float barHeight = 100f; [SerializeField] private float barAnimDuration = 0.6f; [Header("Subtitles")] [SerializeField] private Text subtitleText; [Header("Input")] [SerializeField] private MonoBehaviour playerController; [Header("Data")] [SerializeField] private List cutscenes = new List(); private bool _playing; public bool IsPlaying => _playing; public event Action OnCutsceneStart; public event Action OnCutsceneEnd; public void Play(string id) {{ Cutscene cs = cutscenes.Find(c => c.id == id); if (cs == null) return; StartCoroutine(PlayRoutine(cs)); }} private IEnumerator PlayRoutine(Cutscene cs) {{ _playing = true; OnCutsceneStart?.Invoke(cs.id); if (playerController != null) playerController.enabled = false; Cursor.visible = false; yield return AnimateBars(true); foreach (var shot in cs.shots) {{ if (shot.camera != null) shot.camera.gameObject.SetActive(true); if (shot.actorAnimator != null && shot.actorAnimation != null) shot.actorAnimator.Play(shot.actorAnimation.name); if (subtitleText != null) subtitleText.text = shot.subtitle; yield return new WaitForSeconds(shot.duration); if (shot.camera != null) shot.camera.gameObject.SetActive(false); }} yield return AnimateBars(false); if (playerController != null) playerController.enabled = true; _playing = false; OnCutsceneEnd?.Invoke(cs.id); }} private IEnumerator AnimateBars(bool show) {{ float from = show ? 0f : barHeight; float to = show ? barHeight : 0f; float t = 0f; while (t < barAnimDuration) {{ t += Time.deltaTime; float h = Mathf.Lerp(from, to, t / barAnimDuration); if (topBar != null) topBar.sizeDelta = new Vector2(0, h); if (bottomBar != null) bottomBar.sizeDelta = new Vector2(0, h); yield return null; }} }} }} }} ''') # =========================================================================== # 40. Multiplayer netcode # =========================================================================== @register_tool class CreateMultiplayerNetcodeTool(BaseUnityTool): """Generate multiplayer netcode stubs with sync and voice hooks.""" name = "create_multiplayer_netcode" description = ( "Generate multiplayer netcode: network sync, player spawning, " "voice chat hooks." ) input_schema = { "type": "object", "properties": { "class_name": {"type": "string", "default": "NetworkManager"}, }, } def run(self, class_name: str = "NetworkManager", **_: Any) -> str: return self._wrap(f''' using System; using System.Collections.Generic; using UnityEngine; namespace OpenWorld.Net {{ /// /// Lightweight host/client networking stub. Provides RPC-style /// send/receive, authoritative object spawning, interpolation for /// remote player transforms and voice-chat hook events. Designed as a /// drop-in surface that a real transport (Netcode for GameObjects / /// Mirror) can replace. /// public class {class_name} : MonoBehaviour {{ public static {class_name} Instance {{ get; private set; }} [Header("Config")] [SerializeField] private int port = 7777; [SerializeField] private int maxPlayers = 32; [SerializeField] private GameObject playerPrefab; [SerializeField] private Transform[] spawnPoints; [Header("State")] public bool isHost; public bool isClient; public int localPlayerId = -1; private readonly Dictionary _players = new Dictionary(); private float _sendInterval = 0.05f; private float _sendTimer; public event Action OnPlayerJoined; public event Action OnPlayerLeft; public event Action OnVoiceData; private void Awake() {{ Instance = this; }} public void StartHost() {{ isHost = true; isClient = true; localPlayerId = 0; SpawnPlayer(localPlayerId, true); }} public void StartClient(string address) {{ isHost = false; isClient = true; // Transport connect here. }} public void Stop() {{ foreach (var p in _players.Values) if (p != null) Destroy(p.gameObject); _players.Clear(); isHost = isClient = false; }} private void SpawnPlayer(int id, bool isLocal) {{ if (playerPrefab == null) return; Transform sp = spawnPoints.Length > 0 ? spawnPoints[id % spawnPoints.Length] : transform; GameObject go = Instantiate(playerPrefab, sp.position, sp.rotation); var np = go.AddComponent(); np.Initialize(id, isLocal); _players[id] = np; OnPlayerJoined?.Invoke(id); }} public void SendVoice(int fromId, byte[] data) {{ OnVoiceData?.Invoke(fromId, data); // Broadcast to other players via transport. }} private void Update() {{ if (!isClient) return; _sendTimer -= Time.deltaTime; if (_sendTimer > 0f) return; _sendTimer = _sendInterval; foreach (var p in _players.Values) if (p != null) p.NetUpdate(); }} }} public class NetworkPlayer : MonoBehaviour {{ public int Id {{ get; private set; }} public bool IsLocal {{ get; private set; }} private Vector3 _netPos; private Quaternion _netRot; private float _interpSpeed = 12f; public void Initialize(int id, bool isLocal) {{ Id = id; IsLocal = isLocal; }} public void NetUpdate() {{ if (IsLocal) {{ // Send transform to server (stub). _netPos = transform.position; _netRot = transform.rotation; }} else {{ transform.position = Vector3.Lerp(transform.position, _netPos, _interpSpeed * Time.deltaTime); transform.rotation = Quaternion.Slerp(transform.rotation, _netRot, _interpSpeed * Time.deltaTime); }} }} public void ReceiveSnapshot(Vector3 pos, Quaternion rot) {{ if (IsLocal) return; _netPos = pos; _netRot = rot; }} }} }} ''') # =========================================================================== # 41. Setup Unity project # =========================================================================== @register_tool class SetupUnityProjectTool(BaseUnityTool): """Generate a complete Unity project structure scaffold.""" name = "setup_unity_project" description = ( "Generate a complete Unity project structure: folders, assembly " "definitions, manifest.json and ProjectSettings entries." ) input_schema = { "type": "object", "properties": { "project_name": {"type": "string", "default": "OpenWorldGame"}, "unity_version": {"type": "string", "default": "2022.3.20f1"}, }, } def run(self, project_name: str = "OpenWorldGame", unity_version: str = "2022.3.20f1", **_: Any) -> str: manifest = self._wrap(f''' {{ "dependencies": {{ "com.unity.ai.navigation": "1.1.5", "com.unity.cinemachine": "2.9.7", "com.unity.inputsystem": "1.7.0", "com.unity.postprocessing": "3.2.2", "com.unity.textmeshpro": "3.0.6", "com.unity.ugui": "1.0.0", "com.unity.modules.audio": "1.0.0", "com.unity.modules.imageconversion": "1.0.0", "com.unity.modules.particlesystem": "1.0.0", "com.unity.modules.physics": "1.0.0", "com.unity.modules.terrain": "1.0.0", "com.unity.modules.ui": "1.0.0", "com.unity.ugui": "1.0.0" }} }} ''') asmdef = self._wrap(f''' {{ "name": "OpenWorld", "rootNamespace": "OpenWorld", "references": [], "includePlatforms": [], "excludePlatforms": [], "allowUnsafeCode": false, "overrideReferences": false, "precompiledReferences": [], "autoReferenced": true, "defineConstraints": [], "versionDefines": [], "noEngineReferences": false }} ''') structure = self._wrap(f''' # {project_name} # Unity {unity_version} Directory layout: Assets/ OpenWorld/ Scripts/ Player/ Vehicles/ World/ AI/ Combat/ FX/ UI/ Audio/ Persistence/ Economy/ Missions/ Net/ Prefabs/ Materials/ Models/ Textures/ Audio/ Scenes/ ScriptableObjects/ Settings/ URP/ (or HDRP) ProjectSettings/ Packages/ manifest.json Runtime requirements: - Unity {unity_version} - URP or HDRP - TextMeshPro imported - Input System (both old and new enabled) ''' ) return f"// === manifest.json ===\n{manifest}\n// === OpenWorld.asmdef ===\n{asmdef}\n{structure}" # =========================================================================== # 42. Write C# script # =========================================================================== @register_tool class WriteCsharpScriptTool(BaseUnityTool): """Generate a custom C# script from a template and parameters.""" name = "write_csharp_script" description = ( "Generate a custom C# MonoBehaviour/ScriptableObject from a " "template, namespace and field list." ) input_schema = { "type": "object", "properties": { "class_name": {"type": "string"}, "namespace": {"type": "string", "default": "OpenWorld.Custom"}, "base_class": {"type": "string", "default": "MonoBehaviour"}, "fields": {"type": "array", "items": {"type": "string"}}, }, } def run( self, class_name: str = "CustomScript", namespace: str = "OpenWorld.Custom", base_class: str = "MonoBehaviour", fields: Optional[List[str]] = None, **_: Any, ) -> str: fields = fields or [] field_lines = [] for f in fields: parts = f.split() if len(parts) >= 2: ftype, fname = parts[0], parts[1] field_lines.append(f" [SerializeField] private {ftype} {fname};") field_block = "\n".join(field_lines) if field_lines else " // (no fields)" return self._wrap(f''' using UnityEngine; namespace {namespace} {{ /// /// Auto-generated {class_name}. Extend the lifecycle methods as needed. /// public class {class_name} : {base_class} {{ {field_block} private void Awake() {{ // Cache components here. }} private void Start() {{ // Initialization after Awake. }} private void Update() {{ // Per-frame logic. }} private void FixedUpdate() {{ // Physics-step logic. }} private void LateUpdate() {{ // Post-update (camera, follow). }} private void OnEnable() {{ // Subscribe to events. }} private void OnDisable() {{ // Unsubscribe from events. }} private void OnDestroy() {{ // Cleanup resources. }} }} }} ''') # =========================================================================== # 43. Write scene file # =========================================================================== @register_tool class WriteSceneFileTool(BaseUnityTool): """Generate a minimal Unity scene (.unity) YAML file.""" name = "write_scene_file" description = "Generate a minimal Unity scene YAML file with a Camera and directional light." input_schema = { "type": "object", "properties": { "scene_name": {"type": "string", "default": "MainScene"}, }, } def run(self, scene_name: str = "MainScene", **_: Any) -> str: return self._wrap(f'''%YAML 1.1 %TAG !u! tag:unity3d.com,2011: --- !u!29 &1 OcclusionCullingSettings: m_ObjectHideFlags: 0 serializedVersion: 2 m_OcclusionBakeSettings: smallestOccluder: 5 smallestHole: 0.25 backfaceThreshold: 100 m_SceneGUID: 00000000000000000000000000000000 m_OcclusionCullingData: {{fileID: 0}} --- !u!104 &2 RenderSettings: m_ObjectHideFlags: 0 serializedVersion: 9 m_Fog: 0 m_FogColor: {{r: 0.5, g: 0.5, b: 0.5, a: 1}} m_FogMode: 3 m_FogDensity: 0.01 m_LinearFogStart: 0 m_LinearFogEnd: 300 m_AmbientSkyColor: {{r: 0.2, g: 0.2, b: 0.2, a: 1}} m_AmbientGroundColor: {{r: 0.2, g: 0.2, b: 0.2, a: 1}} m_SkyboxMaterial: {{fileID: 0}} m_HaloStrength: 0.5 m_FlareStrength: 1 m_FlareFadeSpeed: 3 m_HaloTexture: {{fileID: 0}} m_SpotCookie: {{fileID: 0}} m_DefaultReflectionMode: 0 m_DefaultReflectionResolution: 128 m_ReflectionBounces: 1 m_ReflectionIntensity: 1 m_CustomReflection: {{fileID: 0}} m_Sun: {{fileID: 0}} m_IndirectSpecularColor: {{r: 0, g: 0, b: 0, a: 1}} --- !u!157 &3 LightmapSettings: m_ObjectHideFlags: 0 serializedVersion: 11 m_GIWorkflowMode: 0 m_GISettings: serializedVersion: 2 m_BounceScale: 1 m_IndirectOutputScale: 1 m_AlbedoBoost: 1 m_TemporalCoherenceThreshold: 1 m_EnvironmentLightingMode: 0 m_EnableBakedLightmaps: 1 m_EnableRealtimeLightmaps: 1 m_LightmapEditorSettings: serializedVersion: 9 m_Resolution: 2 m_BakeResolution: 40 m_TextureWidth: 1024 m_TextureHeight: 1024 m_AO: 0 m_AOMaxDistance: 1 m_CompAOExponent: 1 m_CompAOExponentDirect: 0 m_Padding: 2 m_LightmapParameters: {{fileID: 0}} m_LightmapsBakeMode: 1 m_TextureCompression: 1 m_FinalGather: 0 m_FinalGatherFiltering: 1 m_FinalGatherRayCount: 256 m_ReflectionCompression: 2 m_LightingDataAsset: {{fileID: 0}} m_UseShadowmask: 1 --- !u!196 &4 NavMeshSettings: serializedVersion: 2 m_ObjectHideFlags: 0 m_BuildSettings: serializedVersion: 2 agentTypeID: 0 agentRadius: 0.5 agentHeight: 2 agentSlope: 45 agentClimb: 0.4 ledgeDropHeight: 0 maxJumpAcrossDistance: 0 minRegionArea: 2 manualCellSize: 0 cellSize: 0.16666667 manualTileSize: 0 tileSize: 256 accuratePlacement: 0 debug: m_Flags: 0 m_NavMeshData: {{fileID: 0}} --- !u!1 &100000 GameObject: m_ObjectHideFlags: 0 m_Name: MainCamera m_IsActive: 1 --- !u!20 &100001 Camera: m_ObjectHideFlags: 0 m_ClearFlags: 1 m_BackGroundColor: {{r: 0.192, g: 0.302, b: 0.474, a: 0.019}} m_FieldOfView: 60 m_NearClipPlane: 0.3 m_FarClipPlane: 1000 --- !u!1 &200000 GameObject: m_ObjectHideFlags: 0 m_Name: DirectionalLight m_IsActive: 1 --- !u!108 &200001 Light: m_ObjectHideFlags: 0 m_Type: 1 m_Color: {{r: 1, g: 0.956, b: 0.839, a: 1}} m_Intensity: 1.2 m_Shadows: m_Type: 2 ''') + f"\n// Scene: {scene_name}\n" # =========================================================================== # 44. Generate complete game # =========================================================================== @register_tool class GenerateCompleteGameTool(BaseUnityTool): """Generate a complete game by orchestrating every tool in the registry.""" name = "generate_complete_game" description = ( "Run every registered tool and assemble a complete Unity project " "in the given output directory." ) input_schema = { "type": "object", "properties": { "output_dir": {"type": "string", "default": "./OpenWorldGame"}, "project_name": {"type": "string", "default": "OpenWorldGame"}, "include_multiplayer": {"type": "boolean", "default": False}, }, } def run( self, output_dir: str = "./OpenWorldGame", project_name: str = "OpenWorldGame", include_multiplayer: bool = False, **_: Any, ) -> str: # Build a manifest of generated files by invoking each tool. manifest = [f"# Generated game: {project_name}", f"# Output: {output_dir}", ""] skip = set() if not include_multiplayer: skip.add("create_multiplayer_netcode") for tool in TOOL_REGISTRY: if tool.name in skip or tool.name == self.name: continue try: code = tool.run() rel = f"Assets/OpenWorld/Scripts/{tool.name}.cs" manifest.append(f"- {rel} ({len(code.splitlines())} lines)") except Exception as exc: # pragma: no cover manifest.append(f"- ERROR in {tool.name}: {exc}") manifest.append("") manifest.append(f"Total tools invoked: {sum(1 for t in TOOL_REGISTRY if t.name not in skip and t.name != self.name)}") return "\n".join(manifest) + "\n" # =========================================================================== # 45. Loot system # =========================================================================== @register_tool class CreateLootSystemTool(BaseUnityTool): """Generate a loot system with rarity tiers and containers.""" name = "create_loot_system" description = ( "Generate a loot system with random loot drops, rarity tiers and " "containers." ) input_schema = { "type": "object", "properties": { "class_name": {"type": "string", "default": "LootSystem"}, }, } def run(self, class_name: str = "LootSystem", **_: Any) -> str: return self._wrap(f''' using System; using System.Collections.Generic; using UnityEngine; namespace OpenWorld.Items {{ /// /// Loot table & container system. Each table maps item IDs to weighted /// drop chances per rarity tier. Containers (chests, corpses) roll the /// table to populate contents on first open. /// public class {class_name} : MonoBehaviour {{ public static {class_name} Instance {{ get; private set; }} public enum Rarity {{ Common, Uncommon, Rare, Epic, Legendary }} [Serializable] public class LootEntry {{ public string itemId; public Rarity rarity; public int minStack = 1; public int maxStack = 1; public float weight = 1f; }} [Serializable] public class LootTable {{ public string id; public List entries = new List(); public int minDrops = 1; public int maxDrops = 3; [NonSerialized] public float totalWeight; }} [Header("Color By Rarity")] [SerializeField] private Color commonColor = Color.gray; [SerializeField] private Color uncommonColor = Color.green; [SerializeField] private Color rareColor = Color.cyan; [SerializeField] private Color epicColor = new Color(0.6f, 0.2f, 0.9f); [SerializeField] private Color legendaryColor = new Color(1f, 0.6f, 0.1f); [Header("Tables")] [SerializeField] private List tables = new List(); private readonly Dictionary> _generated = new Dictionary>(); private void Awake() {{ Instance = this; }} public List Roll(string tableId) {{ LootTable table = tables.Find(t => t.id == tableId); if (table == null) return new List(); RecomputeWeights(table); int count = UnityEngine.Random.Range(table.minDrops, table.maxDrops + 1); List result = new List(); for (int i = 0; i < count; i++) {{ float r = UnityEngine.Random.value * table.totalWeight; float acc = 0f; foreach (var e in table.entries) {{ acc += e.weight; if (r <= acc) {{ result.Add(new LootEntry {{ itemId = e.itemId, rarity = e.rarity, minStack = UnityEngine.Random.Range(e.minStack, e.maxStack + 1), maxStack = e.maxStack, weight = e.weight }}); break; }} }} }} return result; }} public Color RarityColor(Rarity r) {{ return r switch {{ Rarity.Common => commonColor, Rarity.Uncommon => uncommonColor, Rarity.Rare => rareColor, Rarity.Epic => epicColor, Rarity.Legendary => legendaryColor, _ => Color.white, }}; }} private void RecomputeWeights(LootTable table) {{ table.totalWeight = 0f; foreach (var e in table.entries) table.totalWeight += e.weight; }} }} public class LootContainer : MonoBehaviour {{ [SerializeField] private string tableId; [SerializeField] private bool opened; [SerializeField] private GameObject lid; // animates open public event Action> OnOpened; public void Interact() {{ if (opened) return; opened = true; if (lid != null) lid.transform.localRotation = Quaternion.Euler(-90f, 0f, 0f); var loot = LootSystem.Instance?.Roll(tableId) ?? new List(); OnOpened?.Invoke(loot); }} }} }}''').replace("CreateLootSystemTool.LootEntry", "LootSystem.LootEntry") # =========================================================================== # 46. Crafting system # =========================================================================== @register_tool class CreateCraftingSystemTool(BaseUnityTool): """Generate a crafting system that combines items into new ones.""" name = "create_crafting_system" description = "Generate a crafting system that combines items into new ones." input_schema = { "type": "object", "properties": { "class_name": {"type": "string", "default": "CraftingSystem"}, }, } def run(self, class_name: str = "CraftingSystem", **_: Any) -> str: return self._wrap(f''' using System; using System.Collections.Generic; using UnityEngine; namespace OpenWorld.Items {{ /// /// Recipe-driven crafting. Each recipe lists ingredient items/counts /// and a resulting item. The Craft method consumes ingredients from an /// IInventoryProvider and produces the output. /// public class {class_name} : MonoBehaviour {{ [Serializable] public class Ingredient {{ public string itemId; public int count = 1; }} [Serializable] public class Recipe {{ public string id; public string resultItemId; public int resultCount = 1; public List ingredients = new List(); public float craftTime = 2f; public bool requiresWorkbench; }} public interface IInventoryProvider {{ int Count(string itemId); void Remove(string itemId, int count); void Add(string itemId, int count); }} [Header("Recipes")] [SerializeField] private List recipes = new List(); public IReadOnlyList Recipes => recipes; public bool CanCraft(Recipe recipe, IInventoryProvider inv) {{ if (recipe == null || inv == null) return false; foreach (var ing in recipe.ingredients) if (inv.Count(ing.itemId) < ing.count) return false; return true; }} public bool Craft(Recipe recipe, IInventoryProvider inv) {{ if (!CanCraft(recipe, inv)) return false; foreach (var ing in recipe.ingredients) inv.Remove(ing.itemId, ing.count); inv.Add(recipe.resultItemId, recipe.resultCount); return true; }} public Recipe FindByResult(string itemId) => recipes.Find(r => r.resultItemId == itemId); }} }} ''') # =========================================================================== # 47. Skill tree # =========================================================================== @register_tool class CreateSkillTreeTool(BaseUnityTool): """Generate a skill tree with XP, levels and unlockable abilities.""" name = "create_skill_tree" description = ( "Generate a skill tree with XP, levels and unlockable abilities." ) input_schema = { "type": "object", "properties": { "class_name": {"type": "string", "default": "SkillTree"}, }, } def run(self, class_name: str = "SkillTree", **_: Any) -> str: return self._wrap(f''' using System; using System.Collections.Generic; using UnityEngine; namespace OpenWorld.Skills {{ /// /// Player progression. Awards XP from gameplay; levels up at thresholds /// granting skill points. Skill points unlock nodes in a tree; each /// node may require prerequisites and apply stat modifiers (events). /// public class {class_name} : MonoBehaviour {{ public static {class_name} Instance {{ get; private set; }} [Serializable] public class SkillNode {{ public string id; public string displayName; [TextArea] public string description; public int cost = 1; public string[] prerequisites; public float statModifier = 0.1f; public string statKey; // e.g. "max_health", "move_speed" [NonSerialized] public bool unlocked; }} [Serializable] public class SkillBranch {{ public string id; public string name; public List nodes = new List(); }} [Header("Progression")] [SerializeField] private int level = 1; [SerializeField] private int xp; [SerializeField] private int xpToNext = 100; [SerializeField] private float xpGrowth = 1.5f; [SerializeField] private int skillPoints; [Header("Tree")] [SerializeField] private List branches = new List(); [SerializeField] private Dictionary stats = new Dictionary(); public int Level => level; public int Xp => xp; public int SkillPoints => skillPoints; public IReadOnlyList Branches => branches; public event Action OnLevelUp; public event Action OnNodeUnlocked; private void Awake() {{ Instance = this; stats["max_health"] = 100f; stats["move_speed"] = 5f; stats["sprint_speed"] = 8f; }} public void AddXp(int amount) {{ xp += amount; while (xp >= xpToNext) {{ xp -= xpToNext; level++; skillPoints++; xpToNext = Mathf.RoundToInt(xpToNext * xpGrowth); OnLevelUp?.Invoke(level); }} }} public bool UnlockNode(string branchId, string nodeId) {{ SkillBranch branch = branches.Find(b => b.id == branchId); if (branch == null) return false; SkillNode node = branch.nodes.Find(n => n.id == nodeId); if (node == null || node.unlocked || skillPoints < node.cost) return false; if (!PrerequisitesMet(node)) return false; node.unlocked = true; skillPoints -= node.cost; if (!string.IsNullOrEmpty(node.statKey)) stats[node.statKey] += stats.GetValueOrDefault(node.statKey) * node.statModifier; OnNodeUnlocked?.Invoke(node); return true; }} private bool PrerequisitesMet(SkillNode node) {{ if (node.prerequisites == null) return true; foreach (var prereq in node.prerequisites) {{ bool found = false; foreach (var branch in branches) foreach (var n in branch.nodes) if (n.id == prereq && n.unlocked) {{ found = true; break; }} if (!found) return false; }} return true; }} public float GetStat(string key) => stats.GetValueOrDefault(key, 0f); }} }} ''') # =========================================================================== # 48. Faction system # =========================================================================== @register_tool class CreateFactionSystemTool(BaseUnityTool): """Generate a faction system with territories and turf wars.""" name = "create_faction_system" description = ( "Generate a faction system with gang territories, reputation and " "turf wars." ) input_schema = { "type": "object", "properties": { "class_name": {"type": "string", "default": "FactionSystem"}, }, } def run(self, class_name: str = "FactionSystem", **_: Any) -> str: return self._wrap(f''' using System; using System.Collections.Generic; using UnityEngine; namespace OpenWorld.Factions {{ /// /// Faction & territory manager. Each faction has a reputation with the /// player (-100 hostile..+100 allied) and owns territories (zones on /// the map). Turf wars trigger when an attacking faction's power /// exceeds the defender's within a zone. /// public class {class_name} : MonoBehaviour {{ public static {class_name} Instance {{ get; private set; }} [Serializable] public class Faction {{ public string id; public string displayName; public Color color = Color.red; public int power = 100; public int reputation; // -100..100 with player public bool hostileToPlayer; }} [Serializable] public class Territory {{ public string id; public string ownerFactionId; public Bounds bounds; public int defenseValue = 50; }} [Header("Factions")] [SerializeField] private List factions = new List(); [SerializeField] private List territories = new List(); [Header("Turf War")] [SerializeField] private float warCheckInterval = 60f; [SerializeField] private int attackThreshold = 80; private float _warTimer; public IReadOnlyList Factions => factions; public IReadOnlyList Territories => territories; public event Action OnReputationChanged; public event Action OnTurfWarStarted; private void Awake() {{ Instance = this; }} public Faction GetFaction(string id) => factions.Find(f => f.id == id); public void AdjustReputation(string factionId, int delta) {{ Faction f = GetFaction(factionId); if (f == null) return; f.reputation = Mathf.Clamp(f.reputation + delta, -100, 100); f.hostileToPlayer = f.reputation < -25; OnReputationChanged?.Invoke(f, f.reputation); }} public Territory GetTerritoryAt(Vector3 pos) {{ foreach (var t in territories) if (t.bounds.Contains(pos)) return t; return null; }} private void Update() {{ _warTimer -= Time.deltaTime; if (_warTimer > 0f) return; _warTimer = warCheckInterval; EvaluateTurfWars(); }} private void EvaluateTurfWars() {{ foreach (var territory in territories) {{ Faction owner = GetFaction(territory.ownerFactionId); if (owner == null) continue; foreach (var attacker in factions) {{ if (attacker.id == owner.id) continue; if (attacker.power - territory.defenseValue > attackThreshold) {{ OnTurfWarStarted?.Invoke(attacker, owner, territory); ResolveWar(attacker, owner, territory); break; }} }} }} }} private void ResolveWar(Faction attacker, Faction defender, Territory territory) {{ if (attacker.power > defender.power + territory.defenseValue) {{ territory.ownerFactionId = attacker.id; attacker.power -= 30; defender.power -= 20; }} else {{ attacker.power -= 20; defender.power -= 10; }} }} }} }} ''') # =========================================================================== # 49. Stunt system # =========================================================================== @register_tool class CreateStuntSystemTool(BaseUnityTool): """Generate a stunt system with jumps, flips, points and slow-mo.""" name = "create_stunt_system" description = ( "Generate a stunt system with jumps, flips, points and slow-mo " "replays." ) input_schema = { "type": "object", "properties": { "class_name": {"type": "string", "default": "StuntSystem"}, }, } def run(self, class_name: str = "StuntSystem", **_: Any) -> str: return self._wrap(f''' using System; using System.Collections; using UnityEngine; namespace OpenWorld.Gameplay {{ /// /// Detects vehicle stunts (jumps, flips, air-time, near-miss) and /// awards points. On a big stunt triggers a slow-mo window and an /// event for the replay system to capture. /// [RequireComponent(typeof(Rigidbody))] public class {class_name} : MonoBehaviour {{ [Header("Detection")] [SerializeField] private float airTimeThreshold = 0.5f; [SerializeField] private float flipThreshold = 360f; [SerializeField] private float nearMissDistance = 2.5f; [SerializeField] private float nearMissSpeed = 15f; [SerializeField] private LayerMask groundMask; [Header("Scoring")] [SerializeField] private int airTimePoints = 50; [SerializeField] private int flipPoints = 200; [SerializeField] private int nearMissPoints = 100; [SerializeField] private int bigStuntThreshold = 500; [Header("Slow-Mo")] [SerializeField] private float slowMoScale = 0.3f; [SerializeField] private float slowMoDuration = 1.5f; [SerializeField] private float transitionSpeed = 4f; public int TotalPoints {{ get; private set; }} public int CurrentCombo {{ get; private set; }} private Rigidbody _rb; private bool _airborne; private float _airTime; private float _yawAccumulator; private float _lastYaw; private float _comboTimer; public event Action OnStuntScored; // points, label public event Action OnBigStunt; private void Awake() {{ _rb = GetComponent(); _lastYaw = transform.eulerAngles.y; }} private void Update() {{ CheckAirTime(); CheckFlips(); CheckNearMiss(); UpdateCombo(); }} private void CheckAirTime() {{ bool grounded = Physics.Raycast(transform.position, -transform.up, 1.2f, groundMask); if (!grounded) {{ _airTime += Time.deltaTime; if (!_airborne && _airTime >= airTimeThreshold) {{ _airborne = true; }} }} else if (_airborne) {{ ScoreStunt(airTimePoints, "Air Time x" + Mathf.RoundToInt(_airTime)); _airTime = 0f; _airborne = false; }} }} private void CheckFlips() {{ float yaw = transform.eulerAngles.y; float delta = Mathf.DeltaAngle(_lastYaw, yaw); _yawAccumulator += Mathf.Abs(delta); _lastYaw = yaw; if (_yawAccumulator >= flipThreshold) {{ ScoreStunt(flipPoints, "Barrel Roll!"); _yawAccumulator = 0f; }} }} private void CheckNearMiss() {{ if (_rb.velocity.magnitude < nearMissSpeed) return; Collider[] hits = Physics.OverlapSphere(transform.position, nearMissDistance); foreach (var h in hits) {{ if (h.transform == transform) continue; if (h.CompareTag("Vehicle")) {{ ScoreStunt(nearMissPoints, "Near Miss"); return; }} }} }} private void ScoreStunt(int points, string label) {{ CurrentCombo++; int total = points * CurrentCombo; TotalPoints += total; _comboTimer = 3f; OnStuntScored?.Invoke(total, label); if (total >= bigStuntThreshold) StartCoroutine(BigStuntSlowMo()); }} private void UpdateCombo() {{ if (_comboTimer <= 0f) {{ CurrentCombo = 0; return; }} _comboTimer -= Time.deltaTime; }} private IEnumerator BigStuntSlowMo() {{ OnBigStunt?.Invoke(TotalPoints); float original = Time.timeScale; Time.timeScale = slowMoScale; yield return new WaitForSecondsRealtime(slowMoDuration); float t = 0f; while (t < transitionSpeed) {{ t += Time.unscaledDeltaTime; Time.timeScale = Mathf.Lerp(slowMoScale, original, t / transitionSpeed); yield return null; }} Time.timeScale = original; }} }} }} ''') # =========================================================================== # 50. Photo mode # =========================================================================== @register_tool class CreatePhotoModeTool(BaseUnityTool): """Generate a photo mode with free camera, filters and screenshots.""" name = "create_photo_mode" description = ( "Generate a photo mode with free camera, filters and screenshot " "export." ) input_schema = { "type": "object", "properties": { "class_name": {"type": "string", "default": "PhotoMode"}, }, } def run(self, class_name: str = "PhotoMode", **_: Any) -> str: return self._wrap(f''' using System; using System.IO; using UnityEngine; using UnityEngine.PostProcessing; namespace OpenWorld.UI {{ /// /// Photo mode. On activation, detaches the camera into a free-fly /// state, exposes filter presets and captures screenshots to disk via /// ReadPixels. Press F to capture, Esc to exit. /// public class {class_name} : MonoBehaviour {{ public enum Filter {{ None, Vintage, Noir, Vibrant, Cool, Warm }} [Header("Camera")] [SerializeField] private Camera photoCamera; [SerializeField] private float moveSpeed = 12f; [SerializeField] private float lookSpeed = 2f; [SerializeField] private float rollSpeed = 30f; [SerializeField] private float fovMin = 20f; [SerializeField] private float fovMax = 90f; [Header("Filters")] [SerializeField] private Material[] filterMaterials; [Header("Capture")] [SerializeField] private int captureWidth = 1920; [SerializeField] private int captureHeight = 1080; [SerializeField] private string outputFolder = "Photos"; [Header("UI")] [SerializeField] private GameObject uiRoot; [SerializeField] private MonoBehaviour gameplayController; public bool Active {{ get; private set; }} public Filter CurrentFilter {{ get; private set; }} public event Action OnPhotoSaved; private float _yaw, _pitch, _roll; private void Awake() {{ if (photoCamera == null) photoCamera = Camera.main; if (uiRoot != null) uiRoot.SetActive(false); }} private void Update() {{ if (Input.GetKeyDown(KeyCode.F3)) Toggle(); if (!Active) return; HandleMovement(); HandleLook(); if (Input.GetKeyDown(KeyCode.F)) Capture(); if (Input.GetKeyDown(KeyCode.LeftBracket)) CycleFilter(-1); if (Input.GetKeyDown(KeyCode.RightBracket)) CycleFilter(1); }} public void Toggle() {{ Active = !Active; if (uiRoot != null) uiRoot.SetActive(Active); if (gameplayController != null) gameplayController.enabled = !Active; Cursor.lockState = Active ? CursorLockMode.Locked : CursorLockMode.None; Cursor.visible = !Active; if (Active) {{ _yaw = photoCamera.transform.eulerAngles.y; _pitch = photoCamera.transform.eulerAngles.x; }} }} private void HandleMovement() {{ Vector3 move = new Vector3( Input.GetAxis("Horizontal"), Input.GetKey(KeyCode.Space) ? 1f : Input.GetKey(KeyCode.LeftControl) ? -1f : 0f, Input.GetAxis("Vertical")); float speed = Input.GetKey(KeyCode.LeftShift) ? moveSpeed * 2f : moveSpeed; photoCamera.transform.Translate(move * speed * Time.unscaledDeltaTime, Space.Self); float fov = photoCamera.fieldOfView - Input.GetAxis("Mouse ScrollWheel") * 20f; photoCamera.fieldOfView = Mathf.Clamp(fov, fovMin, fovMax); }} private void HandleLook() {{ _yaw += Input.GetAxis("Mouse X") * lookSpeed; _pitch -= Input.GetAxis("Mouse Y") * lookSpeed; if (Input.GetKey(KeyCode.Q)) _roll += rollSpeed * Time.unscaledDeltaTime; if (Input.GetKey(KeyCode.E)) _roll -= rollSpeed * Time.unscaledDeltaTime; photoCamera.transform.rotation = Quaternion.Euler(_pitch, _yaw, _roll); }} private void CycleFilter(int dir) {{ int count = Enum.GetValues(typeof(Filter)).Length; CurrentFilter = (Filter)(((int)CurrentFilter + dir + count) % count); ApplyFilter(); }} private void ApplyFilter() {{ if (filterMaterials == null || filterMaterials.Length == 0) return; int idx = (int)CurrentFilter; // Apply via a full-screen blit — hook into OnRenderImage. }} private void OnRenderImage(RenderTexture src, RenderTexture dst) {{ if (filterMaterials != null && (int)CurrentFilter < filterMaterials.Length && filterMaterials[(int)CurrentFilter] != null) Graphics.Blit(src, dst, filterMaterials[(int)CurrentFilter]); else Graphics.Blit(src, dst); }} public void Capture() {{ StartCoroutine(CaptureRoutine()); }} private System.Collections.IEnumerator CaptureRoutine() {{ yield return new WaitForEndOfFrame(); RenderTexture rt = new RenderTexture(captureWidth, captureHeight, 24); photoCamera.targetTexture = rt; photoCamera.Render(); RenderTexture.active = rt; Texture2D tex = new Texture2D(captureWidth, captureHeight, TextureFormat.RGB24, false); tex.ReadPixels(new Rect(0, 0, captureWidth, captureHeight), 0, 0); tex.Apply(); photoCamera.targetTexture = null; RenderTexture.active = null; Destroy(rt); string dir = Path.Combine(Application.persistentDataPath, outputFolder); Directory.CreateDirectory(dir); string path = Path.Combine(dir, "photo_" + DateTime.Now.ToString("yyyyMMdd_HHmmss") + ".png"); File.WriteAllBytes(path, tex.EncodeToPNG()); Destroy(tex); OnPhotoSaved?.Invoke(path); }} }} }} ''') # =========================================================================== # Module exports # =========================================================================== __all__ = [ "BaseUnityTool", "TOOL_REGISTRY", "register_tool", "get_tool", "list_tools", # Tool classes (useful for direct instantiation / testing) "CreatePlayerControllerTool", "CreateVehicleControllerTool", "CreateProceduralCityTool", "CreateThirdPersonCameraTool", "CreateFpsControllerTool", "CreateDayNightCycleTool", "CreateAiNpcTool", "CreatePickupSystemTool", "CreateHealthSystemTool", "CreateWeaponSystemTool", "CreateAudioManagerTool", "CreateUiManagerTool", "CreateTerrainGeneratorTool", "CreateWaterShaderTool", "CreateParticleEffectsTool", "CreateSaveSystemTool", "CreateInventorySystemTool", "CreateQuestSystemTool", "CreateBuildingGeneratorTool", "CreateRoadNetworkTool", "CreateWantedSystemTool", "CreateTrafficSystemTool", "CreatePedestrianSystemTool", "CreateEconomySystemTool", "CreatePhoneSystemTool", "CreateRadioSystemTool", "CreateWeaponWheelTool", "CreateMinimapSystemTool", "CreateExplosionSystemTool", "CreateRagdollSystemTool", "CreateClothPhysicsTool", "CreateWeatherSystemTool", "CreateCctvSystemTool", "CreateDialogueSystemTool", "CreateMissionSystemTool", "CreateGarageSystemTool", "CreatePropertySystemTool", "CreateCharacterCustomizationTool", "CreateCutsceneSystemTool", "CreateMultiplayerNetcodeTool", "SetupUnityProjectTool", "WriteCsharpScriptTool", "WriteSceneFileTool", "GenerateCompleteGameTool", "CreateLootSystemTool", "CreateCraftingSystemTool", "CreateSkillTreeTool", "CreateFactionSystemTool", "CreateStuntSystemTool", "CreatePhotoModeTool", ] if __name__ == "__main__": # Quick self-check: print tool count and line counts for each generated file. print(f"Registered tools: {len(TOOL_REGISTRY)}") total = 0 for t in TOOL_REGISTRY: try: code = t.run() lines = code.count("\n") + 1 total += lines print(f" {t.name:32s} -> {lines:5d} lines") except Exception as exc: # pragma: no cover print(f" {t.name:32s} -> ERROR: {exc}") print(f"Total generated C# lines: {total}")