>
Guía 2026-04-14

Crear un sistema judicial y legal para el rol en FiveM

OntelMonke

OntelMonke

Admin y desarrollador de Agency Scripts

Arquitectura del sistema legal

Un sistema judicial y legal eleva un servidor de rol de FiveM desde el típico "polis y ladrones" hasta una sociedad estructurada en la que las leyes pesan y las consecuencias siguen un debido proceso. El sistema abarca órdenes judiciales que la policía usa para autorizar registros y detenciones, un flujo de vistas donde los casos se presentan ante un juez, roles de defensa y acusación para los abogados, un sistema de gestión de pruebas que trackea evidencia física y digital y un marco de sentencias para multas, cárcel y castigos alternativos. La arquitectura se centra en una base de datos de gestión de casos que sigue cada procedimiento desde la primera imputación hasta la lectura de cargos, el juicio y el veredicto. Cada caso enlaza con las partes implicadas, sus roles, las pruebas presentadas y el resultado final. Esos datos persisten de forma permanente y construyen un historial legal del servidor que jugadores y autoridades pueden consultar en futuros procedimientos.

Esquema de base de datos para expedientes legales

Tu base de datos tiene que manejar órdenes judiciales, casos, pruebas y antecedentes. El esquema conecta las investigaciones policiales con los procedimientos judiciales a través de un sistema de pruebas compartido donde los ítems recogidos en una investigación pasan a ser exhibidos en juicio. Diseña las tablas para mantener un rastro de auditoría completo de cada acción en cada procedimiento:

CREATE TABLE IF NOT EXISTS warrants (
    id INT AUTO_INCREMENT PRIMARY KEY,
    type ENUM('arrest', 'search', 'bench') NOT NULL,
    target_citizenid VARCHAR(50) NOT NULL,
    target_name VARCHAR(100) NOT NULL,
    reason TEXT NOT NULL,
    issued_by VARCHAR(50) NOT NULL,
    approved_by VARCHAR(50) DEFAULT NULL,
    status ENUM('pending', 'active', 'executed', 'expired', 'revoked') DEFAULT 'pending',
    issued_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    expires_at TIMESTAMP NULL,
    executed_at TIMESTAMP NULL,
    INDEX idx_target (target_citizenid),
    INDEX idx_status (status)
);

CREATE TABLE IF NOT EXISTS court_cases (
    id INT AUTO_INCREMENT PRIMARY KEY,
    case_number VARCHAR(20) UNIQUE NOT NULL,
    defendant_citizenid VARCHAR(50) NOT NULL,
    defendant_name VARCHAR(100) NOT NULL,
    prosecutor_citizenid VARCHAR(50) DEFAULT NULL,
    defense_citizenid VARCHAR(50) DEFAULT NULL,
    judge_citizenid VARCHAR(50) DEFAULT NULL,
    charges TEXT NOT NULL,
    status ENUM('filed', 'arraignment', 'pretrial', 'trial', 'verdict', 'closed', 'dismissed') DEFAULT 'filed',
    plea ENUM('not_guilty', 'guilty', 'no_contest') DEFAULT NULL,
    verdict ENUM('guilty', 'not_guilty', 'mistrial', 'dismissed') DEFAULT NULL,
    sentence TEXT DEFAULT NULL,
    fine_amount INT DEFAULT 0,
    jail_minutes INT DEFAULT 0,
    filed_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    hearing_scheduled TIMESTAMP NULL,
    closed_at TIMESTAMP NULL,
    INDEX idx_defendant (defendant_citizenid),
    INDEX idx_status (status)
);

CREATE TABLE IF NOT EXISTS evidence (
    id INT AUTO_INCREMENT PRIMARY KEY,
    case_id INT DEFAULT NULL,
    type ENUM('physical', 'digital', 'testimony', 'document', 'photo') NOT NULL,
    label VARCHAR(200) NOT NULL,
    description TEXT DEFAULT NULL,
    collected_by VARCHAR(50) NOT NULL,
    collected_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    chain_of_custody TEXT DEFAULT NULL,
    is_admitted BOOLEAN DEFAULT FALSE,
    FOREIGN KEY (case_id) REFERENCES court_cases(id)
);

CREATE TABLE IF NOT EXISTS legal_records (
    id INT AUTO_INCREMENT PRIMARY KEY,
    citizenid VARCHAR(50) NOT NULL,
    record_type ENUM('conviction', 'acquittal', 'fine', 'warning', 'restraining_order') NOT NULL,
    description TEXT NOT NULL,
    case_id INT DEFAULT NULL,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    expunged BOOLEAN DEFAULT FALSE,
    INDEX idx_citizen (citizenid),
    FOREIGN KEY (case_id) REFERENCES court_cases(id)
);

Sistema de órdenes judiciales

Las órdenes son el puente legal entre la investigación policial y la acción. Una orden de arresto autoriza a los agentes a detener a una persona concreta, una orden de registro permite registrar una propiedad o vehículo y una orden judicial directa se emite cuando alguien no comparece. Los agentes crean peticiones de orden a través del MDT que entran en una cola pendiente de revisión judicial. Un juez u oficial autorizado revisa la petición, examina la descripción de la causa probable y la aprueba o rechaza. Las órdenes aprobadas pasan a estar activas y aparecen en el MDT policial cuando los agentes interactúan con la persona objetivo, avisándoles de que existe una orden. Implementa expiración para que las órdenes no se queden eternamente, con 7 días reales para arresto y 48 horas para registro tras su aprobación:

-- Petición de orden por la policía
RegisterNetEvent('legal:server:requestWarrant', function(data)
    local src = source
    local Officer = QBCore.Functions.GetPlayer(src)
    if not Officer or Officer.PlayerData.job.name ~= 'police' then return end

    local warrant = {
        type = data.type,
        target_citizenid = data.targetCitizenid,
        target_name = data.targetName,
        reason = data.reason,
        issued_by = Officer.PlayerData.citizenid,
    }

    local id = MySQL.insert.await(
        'INSERT INTO warrants (type, target_citizenid, target_name, reason, issued_by) VALUES (?, ?, ?, ?, ?)',
        { warrant.type, warrant.target_citizenid, warrant.target_name, warrant.reason, warrant.issued_by }
    )

    -- Notifica a los jueces
    local players = QBCore.Functions.GetQBPlayers()
    for _, player in pairs(players) do
        if player.PlayerData.job.name == 'judge' then
            TriggerClientEvent('legal:client:warrantPending', player.PlayerData.source, {
                id = id,
                type = warrant.type,
                targetName = warrant.target_name,
                reason = warrant.reason,
                officerName = Officer.PlayerData.charinfo.firstname .. ' ' .. Officer.PlayerData.charinfo.lastname,
            })
        end
    end

    TriggerClientEvent('QBCore:Notify', src, 'Warrant request submitted for judicial review', 'success')
end)

-- Aprobación por el juez
RegisterNetEvent('legal:server:approveWarrant', function(warrantId)
    local src = source
    local Judge = QBCore.Functions.GetPlayer(src)
    if not Judge or Judge.PlayerData.job.name ~= 'judge' then return end

    local warrant = MySQL.single.await('SELECT * FROM warrants WHERE id = ? AND status = "pending"', { warrantId })
    if not warrant then return end

    local expiryHours = warrant.type == 'search' and 48 or 168
    local expiresAt = os.date('!%Y-%m-%d %H:%M:%S', os.time() + (expiryHours * 3600))

    MySQL.update('UPDATE warrants SET status = "active", approved_by = ?, expires_at = ? WHERE id = ?',
        { Judge.PlayerData.citizenid, expiresAt, warrantId })

    -- Notifica al agente solicitante si está online
    TriggerClientEvent('QBCore:Notify', src, 'Warrant #' .. warrantId .. ' approved', 'success')
end)

Flujo de la vista judicial

Las vistas siguen un flujo estructurado que mueve los casos por la lectura de cargos, mociones preliminares, juicio y sentencia. Cuando se presenta un caso, el sistema genera un número único y le asigna el estado "filed". El juez revisa el caso y programa una lectura de cargos donde el acusado declara su culpabilidad. Si se declara no culpable, el caso avanza a fase preliminar donde los abogados presentan mociones y pruebas, y después a juicio donde ambas partes exponen sus argumentos. El juez controla el flujo con comandos que avanzan el estado del caso y disparan actualizaciones de UI para todos los participantes. Implementa una NUI de sala donde se vea la información del caso, la fase actual y controles específicos por rol para juez, fiscal y abogado defensor. El juez dispone de botones de veredicto y campos de sentencia; los abogados, de botones de objeción y controles de presentación de pruebas.

Rol del abogado y sistema de defensa

El trabajo de abogado crea una carrera entera en el servidor con mecánicas propias. Los abogados defensores pueden ser contratados por los acusados a través de un directorio accesible desde la cárcel o desde la app del teléfono. Cuando un abogado acepta un cliente, obtiene acceso al expediente del caso en el sistema legal, incluidas las pruebas presentadas por la acusación. Los abogados pueden presentar mociones para suprimir pruebas, pedir aplazamientos, negociar acuerdos con el fiscal y presentar sus propias pruebas y testigos en juicio. Implementa un examen de colegiación donde los nuevos personajes abogado deban aprobar un test de conocimientos antes de obtener el trabajo, garantizando un mínimo de calidad de rol. Registra estadísticas de los abogados como casos ganados, perdidos y reducción media de condena a sus clientes, creando un mercado competitivo donde los letrados consolidados cobren honorarios más altos:

Config.LawyerSystem = {
    barExamQuestions = 15,
    passingScore = 11,       -- 73% para aprobar
    examCooldown = 86400,    -- 24 horas entre intentos
    maxActiveClients = 5,
    consultationFee = 500,   -- tarifa base por consulta inicial
    trialFeeRange = { min = 2000, max = 25000 },

    motionTypes = {
        { id = 'suppress_evidence', label = 'Motion to Suppress Evidence', description = 'Request exclusion of illegally obtained evidence' },
        { id = 'dismiss_charges', label = 'Motion to Dismiss', description = 'Request dismissal due to insufficient evidence' },
        { id = 'continuance', label = 'Motion for Continuance', description = 'Request to postpone the hearing' },
        { id = 'change_venue', label = 'Motion for Change of Venue', description = 'Request trial at different location' },
        { id = 'bail_reduction', label = 'Motion for Bail Reduction', description = 'Request lower bail amount' },
        { id = 'expungement', label = 'Motion for Expungement', description = 'Request to clear prior conviction' },
    },
}

-- Contratar abogado desde la cárcel
RegisterNetEvent('legal:server:hireLawyer', function(lawyerCitizenid)
    local src = source
    local Player = QBCore.Functions.GetPlayer(src)
    if not Player then return end

    local citizenid = Player.PlayerData.citizenid

    -- Busca el caso activo de este jugador
    local activeCase = MySQL.single.await(
        'SELECT * FROM court_cases WHERE defendant_citizenid = ? AND status NOT IN ("closed", "dismissed") ORDER BY filed_at DESC LIMIT 1',
        { citizenid }
    )
    if not activeCase then
        TriggerClientEvent('QBCore:Notify', src, 'No active case found', 'error')
        return
    end

    -- Asigna abogado al caso
    MySQL.update('UPDATE court_cases SET defense_citizenid = ? WHERE id = ?',
        { lawyerCitizenid, activeCase.id })

    -- Notifica al abogado
    local lawyerPlayer = QBCore.Functions.GetPlayerByCitizenId(lawyerCitizenid)
    if lawyerPlayer then
        TriggerClientEvent('QBCore:Notify', lawyerPlayer.PlayerData.source,
            'New client: ' .. Player.PlayerData.charinfo.firstname .. ' ' .. Player.PlayerData.charinfo.lastname ..
            ' | Case #' .. activeCase.case_number, 'success')
    end

    TriggerClientEvent('QBCore:Notify', src, 'Attorney assigned to your case', 'success')
end)

Sistema de gestión de pruebas

El sistema de pruebas crea una cadena de custodia que conecta la investigación en la escena del crimen con la presentación en sala. Cuando la policía recoge pruebas en una escena con un comando de recogida, el sistema crea un registro con la identidad del agente, marca de tiempo y ubicación. Cada prueba recibe un identificador único que persiste durante todo el proceso legal. Los tipos de prueba incluyen ítems físicos como armas, drogas y ropa, pruebas digitales como registros de teléfono y grabaciones de CCTV, testimonios de testigos registrados como entradas de texto y documentos como registros financieros o IDs falsos. Los abogados pueden impugnar la admisibilidad mediante mociones que cuestionen el método de recogida o la cadena de custodia. El juez revisa estas mociones y puede marcar pruebas como inadmisibles, retirándolas del expediente. Implementa un depósito de pruebas en la comisaría donde se guarden los ítems físicos, usando la integración con el sistema de inventario para controlar los objetos reales recogidos en las investigaciones.

Multas y fianzas

Las multas y fianzas generan consecuencias económicas dentro del sistema legal. Al leerse los cargos, el juez fija una fianza en función de la gravedad y del historial del acusado. El acusado puede pagarla por el sistema judicial para salir en libertad a la espera del juicio o permanecer en la cárcel hasta la vista. El pago de la fianza descuenta del banco del jugador y se devuelve al cerrarse el caso si compareció a todas las vistas. Si no comparece, la fianza se pierde y se emite una orden judicial directa. Las multas se imponen como parte de la sentencia y deben pagarse en un plazo fijado. Las impagadas generan intereses y pueden disparar consecuencias adicionales como suspensión del carné o embargos. Implementa planes de pago para multas grandes donde el acusado pueda pagar cuotas semanales en lugar del total de golpe, añadiendo una obligación financiera persistente que mantenga vigente el sistema legal en el día a día del jugador.

Comandos de juez y sentencias

Los jueces necesitan un juego de comandos completo para gestionar los procedimientos y hacer cumplir el sistema legal. Los comandos principales incluyen /case para ver y gestionar casos, /warrant para aprobar o rechazar órdenes, /sentence para dictar cárcel y multas, /bail para fijar fianzas, /gag para silenciar jugadores que alteren la vista y /contempt para encarcelar a quienes falten al respeto al tribunal. El comando de sentencia debería referenciar una configuración de código penal con mínimos y máximos por delito, asegurando coherencia entre jueces. Implementa un sistema de directrices de sentencia que sugiera penas adecuadas en función de los cargos y el historial del acusado, permitiendo al juez apartarse con justificación. Guarda todas las decisiones judiciales en la tabla de antecedentes para que el servidor vaya construyendo un cuerpo de precedentes que los jugadores experimentados puedan citar. Añade un proceso de apelación judicial donde las sentencias puedan recurrirse ante un juez superior o un tribunal en pleno, creando capas extra de rol legal para los jugadores más dedicados.

Integración con policía, prisión y MDT

El sistema legal debe integrarse sin costuras con los recursos existentes de policía, prisión y MDT para formar una canalización de justicia coherente. Cuando la policía imputa cargos por el MDT, el sistema debería crear automáticamente un caso con el número adecuado y enlazar las pruebas ya recogidas en la investigación. Cuando un juez condena a cárcel, el sistema debería llamar directamente a la función de export del recurso de prisión para que la sentencia fluya de forma automática sin que un agente tenga que ejecutar manualmente un comando de encarcelamiento. Las órdenes activas deberían aparecer en el MDT policial al buscar a un ciudadano, con indicadores claros del tipo, autoridad emisora y fecha de expiración. Los antecedentes penales generados por los veredictos deberían ser accesibles en el MDT bajo el perfil del ciudadano, mostrando su historial legal completo con condenas, absoluciones, casos activos y multas pendientes. Exporta todas las funciones del sistema legal para que otros recursos comprueben estados de órdenes, verifiquen credenciales de abogados, consulten antecedentes e interactúen con la judicatura de forma programática.

Compartir este artículo

¿Listo para mejorar tu servidor?

Echa un vistazo a nuestros scripts premium de FiveM en la tienda de Agency Scripts o únete a nuestra comunidad de Discord para soporte y novedades.