local frases = { --"La velocidad actual es: " .. ac.getCar(0).speedKmh .. " kilómetros por hora.", --"Las RPM del motor son: " .. ac.getCar(0).rpm .. ".", --"Estás en la marcha: " .. ac.getCar(0).gear .. ".", --"El ángulo del volante es: " .. ac.getCar(0).steerAngle .. " grados.", "La temperatura del agua es: " .. math.floor(ac.getCar(0).waterTemperature) .. " grados.", "La temperatura del aceite es: " .. math.floor(ac.getCar(0).oilTemperature) .. " grados..", "La presión de turbo es: " .. ac.getCar(0).turboBoost .. " bar.", "El nivel de combustible actual es de: " .. ac.getCar(0).fuel .. " litros.", "Creemos que la mejor distribución de para este circuito es: " .. math.floor(ac.getCar(1).brakeBias) .. " %.", --"La presión de frenos delanteros es: " .. ac.getCar(0).brakePressFront .. ".", --"La presión de frenos traseros es: " .. ac.getCar(0).brakePressRear .. ".", --"La carga aerodinámica es: " .. ac.getCar(0).aeroForce .. ".", --"El estado del ABS es: " .. (ac.getCar(0).abs and "Activado" or "Desactivado") .. ".", --"El estado del TC es: " .. (ac.getCar(0).tcs and "Activado" or "Desactivado") .. ".", --"La velocidad del coche " .. ac.getCar(1).driverName .. " es: " .. ac.getCar(1).speedKmh .. " kilómetros por hora.", --"El tiempo restante de sesión es: " .. ac.getSessionTimeRemaining() .. " segundos.", --"Estás en la vuelta: " .. ac.getCar(0).currentLapNumber .. ".", --"Tu mejor vuelta es: " .. ac.getCar(0).bestLapTime .. " segundos.", --"La temperatura ambiente es: " .. ac.getAmbientTemperature() .. " grados Celsius.", --"La hora actual es: " .. ac.getHour() .. ":00 horas.", --"Estás en el circuito: " .. ac.getSurfaceName() .. ".", --"La longitud del circuito es: " .. ac.getSurfaceLenght() .. " metros.", --"El límite de velocidad en boxes es: " .. ac.getCarPitLaneSpeed() .. " kilómetros por hora.", --"Estás en el sector: " .. ac.getCarCurrentSectorIndex() .. ".", --"Tu tiempo en el sector 1 es: " .. ac.getCarSector1Time() .. " segundos.", --"Tu tiempo en el sector 2 es: " .. ac.getCarSector2Time() .. " segundos.", --"Tu tiempo en el sector 3 es: " .. ac.getCarSector3Time() .. " segundos.", --"La bandera actual es: " .. ac.getCarYellowFlagLaps() .. ".", --"Has tenido " .. ac.getCarIncidentCount() .. " incidentes en esta carrera.", --"Tu potencia actual del motor es: " .. ac.getCar(0).enginePower .. " caballos de fuerza.", --"El par motor actual es: " .. ac.getCar(0).engineTorque .. " Newton-metros.", --"La temperatura de los neumáticos delanteros es: " .. ac.getCar(0).tireTempFL .. " grados Celsius.", --"La temperatura de los neumáticos traseros es: " .. ac.getCar(0).tireTempRR .. " grados Celsius.", --"El desgaste de los neumáticos delanteros es: " .. ac.getCar(0).tireWearFL .. "%.", --"El desgaste de los neumáticos traseros es: " .. ac.getCar(0).tireWearRR .. "%.", --"La posición actual en la carrera es: " .. ac.getCar(0).currentPosition .. ".", --"El equipo de " .. ac.getCar(idCoche).driverName .. " es: " .. ac.getCar(idCoche).carModel .. ".", --"La distancia al coche " .. ac.getCar(idCoche).driverName .. " es: " .. ac.getCar(idCoche).distanceToPlayer .. " metros.", --"La diferencia de tiempo con el líder es: " .. ac.getCar(idCoche).timeBehindLeader .. " segundos.", --"La última vuelta del coche " .. ac.getCar(idCoche).driverName .. " fue de: " .. ac.getCar(idCoche).lastLap .. " segundos.", --"El tipo de sesión actual es: " .. ac.getSessions() .. ".", --"La cantidad de vueltas de la sesión es: " .. ac.getSessionLaps() .. ".", --"El tiempo total de la sesión es: " .. ac.getSessionTime() .. " minutos.", --"El número total de coches en la sesión es: " .. ac.getCarsCount() .. ".", --"Tu configuración de caja de cambios es: " .. (ac.getCar(0).automaticTransmission and "Automática" or "Manual") .. ".", --"El recorrido de la suspensión delantera es: " .. ac.getCar(0).suspensionTravelFront .. " milímetros.", --"El recorrido de la suspensión trasera es: " .. ac.getCar(0).suspensionTravelRear .. " milímetros.", --"La altura de montura actual es: " .. ac.getCar(0).rideHeight .. " milímetros.", --"La configuración del alerón trasero es: " .. ac.getCar(0).wingSuit .. ".", --"La temperatura de la pista es: " .. ac.getSurfaceTemperature() .. " grados Celsius.", --"Las condiciones de agarre de la pista son: " .. ac.getSurfaceGrip() .. "%.", --"El nivel de humedad de la pista es: " .. ac.getTrackHumidity() .. "%.", --"La probabilidad de lluvia es: " .. ac.getChanceOfRain() .. "%.", --"La dirección del viento es: " .. ac.getWindDirection() .. " grados.", --"La velocidad del viento es: " .. ac.getWindSpeed() .. " kilómetros por hora.", --"Tu diferencia de tiempo con la vuelta ideal es: " .. (ac.getCar(0).idealLapTime - ac.getCar(0).lastLap) .. " segundos.", --"La peor vuelta del coche " .. ac.getCar(idCoche).driverName .. " fue de: " .. ac.getCar(idCoche).worstLapTime .. " segundos.", --"Has completado " .. ac.getCar(0).overtakesCompleted .. " adelantamientos.", --"El desgaste del motor es: " .. ac.getCar(0).engineWear .. "%.", --"El desgaste de los frenos delanteros es: " .. ac.getCar(0).brakeWearFront .. "%.", --"El desgaste de los frenos traseros es: " .. ac.getCar(0).brakeWearRear .. "%.", --"El combustible estimado para terminar la carrera es: " .. ac.getCar(0).fuelEstimatedLaps .. " litros.", --"La ventana de parada en boxes comienza en: " .. ac.getCarPitWindowStart() .. " segundos.", --"La ventana de parada en boxes termina en: " .. ac.getCarPitWindowEnd() .. " segundos.", --"El compuesto de neumáticos disponible en boxes es: " .. ac.getCarPitTireCompound() .. ".", --"Se realizará un cambio de neumáticos en la próxima parada: " .. (ac.getCarPitTireChange() and "Sí" or "No") .. ".", --"La próxima parada en boxes está prevista para la vuelta: " .. ac.getCarPitLapEstimation() .. ".", --"Tu hora de entrada en boxes prevista es: " .. ac.getCarPitEstimatedTime() .. " segundos.", --"Tu ángulo de alerón delantero actual es: " .. ac.getCar(0).wingSuits_f .. " grados.", --"Tu ángulo de alerón trasero actual es: " .. ac.getCar(0).wingSuits_r .. " grados.", --"Tu presión de neumáticos delanteros es: " .. ac.getCar(0).tirePressureFL .. " PSI.", --"Tu presión de neumáticos traseros es: " .. ac.getCar(0).tirePressureRR .. " PSI.", --"La temperatura de la caja de cambios es: " .. ac.getCar(0).transmissionTemperature .. " grados Celsius.", --"La temperatura de los frenos delanteros es: " .. ac.getCar(0).brakeTempFL .. " grados Celsius.", --"La temperatura de los frenos traseros es: " .. ac.getCar(0).brakeTempRR .. " grados Celsius.", --"La velocidad del coche " .. ac.getCar(0).driverName .. " en la última vuelta fue: " .. ac.getCar(0).lastLap .. " segundos.", --"La diferencia de tiempo con el coche " .. ac.getCar(idCoche).driverName .. " es: " .. ac.getCar(idCoche).timeBehindNext .. " segundos.", --"El tiempo de la sesión restante es: " .. ac.getSessionTimeLeft() .. " segundos.", --"Tu tiempo delta con el líder es: " .. ac.getCar(0).timeDeltaLeader .. " segundos.", --"Tu tiempo delta con el coche " .. ac.getCar(idCoche).driverName .. " es: " .. ac.getCar(idCoche).timeDeltaToPlayer .. " segundos.", --"Tu distancia al coche " .. ac.getCar(idCoche).driverName .. " es: " .. ac.getCar(idCoche).distanceToNext .. " metros.", --"Tu distancia al coche de adelante es: " .. ac.getCar(0).distanceToNext .. " metros.", --"Tu distancia al coche de atrás es: " .. ac.getCar(0).distanceToPrev .. " metros.", --"Tu velocidad actual en la recta es: " .. ac.getCar(0).straightLineSpeed .. " kilómetros por hora.", --"Tu velocidad promedio en esta vuelta es: " .. ac.getCar(0).averageLapSpeed .. " kilómetros por hora.", --"Tu velocidad máxima en esta vuelta es: " .. ac.getCar(0).maxLapSpeed .. " kilómetros por hora.", --"Tu velocidad actual en las curvas es: " .. ac.getCar(0).curveSpeed .. " kilómetros por hora.", --"Tu altura sobre el nivel del mar es: " .. ac.getCar(0).elevationAboveNearestRoad .. " metros.", --"Tu inclinación lateral es: " .. ac.getCar(0).rollAngle .. " grados.", --"Tu rotación sobre el eje vertical es: " .. ac.getCar(0).pitchAngle .. " grados.", --"Tu aceleración longitudinal es: " .. ac.getCar(0).accelerationLongitudinal .. " metros por segundo al cuadrado.", --"Tu aceleración lateral es: " .. ac.getCar(0).accelerationLateral .. " metros por segundo al cuadrado.", --"Tu aceleración vertical es: " .. ac.getCar(0).accelerationVertical .. " metros por segundo al cuadrado.", --"Tu torque en las ruedas delanteras es: " .. ac.getCar(0).wheelsTorqueFront .. " Newton-metros.", --"Tu torque en las ruedas traseras es: " .. ac.getCar(0).wheelsTorqueRear .. " Newton-metros.", --"Tu velocidad de giro en las ruedas delanteras es: " .. ac.getCar(0).wheelsSpeedFront .. " revoluciones por minuto.", --"Tu velocidad de giro en las ruedas traseras es: " .. ac.getCar(0).wheelsSpeedRear .. " revoluciones por minuto.", --"Tu ángulo de deslizamiento en las ruedas delanteras es: " .. ac.getCar(0).wheelSlipFront .. " grados.", --"Tu ángulo de deslizamiento en las ruedas traseras es: " .. ac.getCar(0).wheelSlipRear .. " grados.", --"Tu fuerza de frenado en las ruedas delanteras es: " .. ac.getCar(0).wheelBrakeFrontLeft .. " Newton-metros.", --"Tu fuerza de frenado en las ruedas traseras es: " .. ac.getCar(0).wheelBrakeRearLeft .. " Newton-metros." } local ultimaReproduccion = 0 local tiempoEntreReproduccion = 30 -- 60 segundos = 1 minuto local vueltaObligatoriaCambioNeumaticos = 2 -- La vuelta en la que el cambio de neumáticos es obligatorio local indiceNeumaticosInicio = 0 -- El índice del compuesto de neumáticos con el que el jugador debe comenzar local indiceNeumaticosACambiar = 1 -- El índice del compuesto de neumáticos al que el jugador debe cambiar local tiempoEsperadoCambioNeumaticos = 20.8 -- Tiempo esperado para que el auto esté detenido en pits cambiando neumáticos. local compuestosSalidaIA = {} local aiTakePlayersFuel = false -- Cambiar a true para que la IA tome la carga de combustible del jugador. local aiDriverCount = 0 local velocidadiaenvsc = 100 local intervaloComprobacion = 180 -- 3 minutos expresados en segundos local activeFlag = nil colisionPreviaConCoche = false -- Variables configurables para detectar donuts --umbralGrados = 95 -- activar el VSC si se detecta un cambio de dirección de 180 grados o más en un lapso de tiempo configurable en tiempoMaximoCambioCompass --tiempoMaximoCambioCompass = 3.2 -- Tiempo máximo (en segundos) permitido entre cambios de dirección antes de activar el VSC -- Supongamos que hay una variable para mantener el estado de si el piloto ya tiene un DT local tieneDT = false local cortesDespuesDeDT = 0 -- Nueva variable para contar los cortes después del DT local ultimoCorteRegistrado = -1 -- Para que la comprobación funcione la primera vez local ventanaAveriaActiva = false local windowId = "VentanaAveria" local windowPos = vec2(200, 200) -- Posición de la ventana local windowSize = vec2(400, 200) -- Tamaño de la ventana -- Variables globales local neumaticosVerificados = false local velocidadParaVerificarNeumaticos = 80 -- La velocidad a la que decidimos verificar el neumático local mensajeFinalMostrado = false local mostrarMensajeFinal = false local mensajeActualParaPiloto = "" local jugadorEntroAPitsEnVueltaObligatoria = false local audiosReproducidos = {} local audiosReproducidos = {} local audioActual = nil -- La referencia al audio que se está reproduciendo actualmente local tiempoInicioAudio = 0 -- El tiempo cuando el audio actual comenzó a reproducirse local duracionAudio = 0 -- La duración estimada del audio actual en segundos local colaReproduccion = {} -- Nueva cola de reproducción local reproduciendo = false -- Estado de reproducción local audioAnterior = nil local conteoIAs = 0 local primeraVez = true local estrategiasParadaIAs = {} local tiempoParadaEnPitsParaCambioNeumaticosIA = {} local tiempoDetenidoEnPitsIA = {} local neumaticosInicialesIA = {} local neumaticosAsignados = false local tiempoUltimaComprobacion = 0 -- Guarda el tiempo desde la última comprobación de rotura local cocheRoto = false -- Estado del coche, si se ha roto o no local mensajedeaveria ="" local avisoUltimaOportunidadEmitido = false local ultimoTiempoDeActualizacion = 0 intervaloDeActualizacion = 1.0 -- Por ejemplo, 1 segundo startTime = nil local mostrarMensajeVueltaObligatoria = false --physics.setCarPenalty(ac.PenaltyType.MandatoryPits,2)--esto es para poner un DT drive trought --local isCSPCompatible = ac.getPatchVersionCode and ac.getPatchVersionCode() >= 2219 local ultimoAudioReproducido = nil -- Variables globales para el manejo del VSC local prevCollisionDepth = 0 local prevCollisionDepth = {} local prevHitValue = {} local HitValue = 0 local carsCount = ac.getSim().carsCount -- Obtiene el número total de coches for i = 1, carsCount do prevCollisionDepth[i] = 0 prevHitValue[i] = 0 end isVSCActive = false playerPositionAtVSC = nil vscTimer = 0 -- Temporizador desde la activación del VSC vscTimerActive = false -- Estado del temporizador vscActivationTime = nil gracePeriod = 50 -- 90 segundos de virtualsafetycar local tiempoTranscurridoVSC = 0 local imagenamostrar ="ingeniero.png" -- Variables de configuración detectar giros bruscos donuts roscos local muestra1 = nil local ultimoTiempo = os.clock() local accidenteReciente = false posicionesEnVSC = nil -- Almacenará las posiciones de los coches cuando se active el VSC playerPositionAtVSC = 0 -- La posición del jugador cuando se activó el VSC isVSCActive = false -- Estado de activación del VSC mejorPosicionObjetivoVSC = nil -- Para rastrear la mejor posición objetivo durante el VSC ---compass borrar todo no lo uso local mensajeActualParaPilotocierrepits ="" -- Inicialización de la cola de mensajes y variables de control local colaDeMensajes = {} local mensajeMostrando = false local tiempoMensaje = 3 local duracionMensaje = 0.01 -- Duración de cada mensaje en segundos local settings = { autopilot = {enabled = true, Speed = 100, Agression = 100, Pitnow = false}, initialized = false, timerStarted = false, timer = 0 } settings.timerStarted = true settings.timer = 0 local sesionActual local sesionAnterior local sessionRestarted = false -- function mostrarVentanaAveria() -- ventanaAveriaActiva = true -- ui.transparentWindow(windowId, windowPos, windowSize, true, false, function() -- ui.pushDWriteFont('Segoe UI;Weight=Bold') -- ui.dwriteText("AVERÍA IMPORTANTE - ENTRA EN BOXES INMEDIATAMENTE", 24) -- ui.popDWriteFont() -- ui.image('averia.png', vec2(10, 10)) -- Muestra una imagen de bandera roja -- end) -- end function ocultarVentanaAveria() ventanaAveriaActiva = false end function detectarVuelcos() -- Sistema de detección de vuelcos de cualquier coche local carsCountvolcar = ac.getSim().carsCount for i = 0, carsCountvolcar - 1 do --local carvolcar = ac.getCar(i) local estadoUp = ac.getCar(i).up.y if estadoUp < 0 then --ac.log("estadoUP: " .. tostring(estadoUp)) --reproducirAudio('accid.mp3') reproducirAudio('accid.mp3', 7) setTimeout(function() activarVSC(i) end, 6) -- El retraso está en segundos end end end function detectarCortesYDT() -- Sistema de detección de cortes y DT (drive through) local cortes = ac.getCar(0).lapCutsCount if tieneDT then -- Ajustar el conteo de cortes para reflejar los cortes después del DT cortes = cortes - 4 end local cortesDespuesDeDT = 0 -- Mantener el conteo de cortes después del DT if cortes ~= ultimoCorteRegistrado then -- Actualiza el último corte registrado ultimoCorteRegistrado = cortes -- Actualizar el mensaje para el piloto if tieneDT then mensajeActualParaPiloto = "CUMPLE EL DRIVE-THROUGH" else if ac.getSim().raceSessionType == ac.SessionType.Practice then imagenamostrar="ingeniero.png" mensajeActualParaPiloto = "SESIÓN DE PRÁCTICA, DISFRUTA DEL CIRCUITO. " .. "Cortes: " .. (cortes) elseif ac.getSim().raceSessionType == ac.SessionType.Qualify then imagenamostrar="ingeniero.png" mensajeActualParaPiloto = "INTENTA HACER LA MEJOR QUALY POSIBLE PARA EL EQUIPO. " .. "Cortes: " .. (cortes) end end if not tieneDT then if cortes >= 4 then physics.setCarPenalty(ac.PenaltyType.MandatoryPits, 2) tieneDT = true -- Marcar que el piloto ha recibido un DT cortesDespuesDeDT = 0 -- Resetear el conteo de cortes después del DT --reproducirAudio('cortes7_dt.mp3') -- Reproducir audio para el DT reproducirAudio('cortes7_dt.mp3', 14) elseif cortes == 3 then --reproducirAudio('cortes6ultimocorte.mp3') -- Reproducir audio del último corte antes del DT reproducirAudio('cortes6ultimocorte.mp3', 5) elseif cortes == 2 then --reproducirAudio('cortes3.mp3') -- Reproducir audio del último corte antes del DT reproducirAudio('cortes3.mp3', 3) elseif cortes == 1 then --reproducirAudio('cortes1.mp3') -- Reproducir audio del último corte antes del DT reproducirAudio('cortes1.mp3', 3) end else -- Si el piloto ya tiene un DT, ajustar el conteo de cortes después del DT cortesDespuesDeDT = cortes if cortesDespuesDeDT >= 2 then physics.setCarPenalty(ac.PenaltyType.BlackFlag, -1) reproducirAudio('cortes8_black.mp3',10) -- Reproducir audio de expulsión cortes=0 --para que deje de sonar el audio. elseif cortesDespuesDeDT == 1 then reproducirAudio('cortes8a_oportunidad.mp3',6) -- Reproducir audio de última oportunidad end end end end -- Función para detectar giros bruscos y activar el VSC function detectarGiroBruscoYActivarVSC() local car = ac.getCar(0) -- Coche con índice 0 local lookActual = car.look -- Vector look actual local tiempoActual = os.clock() local tiempoTranscurrido = tiempoActual - ultimoTiempo local intervaloMinimo = 1.2 -- Tiempo mínimo en segundos para detectar el cambio -- Verificar si ha habido un accidente a más de 180 de mi coche, para sacar el vsc local collisionDepthChange = car.collisionDepth - (prevCollisionDepth[1] or 0) if collisionDepthChange > 0 and car.collidedWith ~= 0 and car.speedKmh > 180 then accidenteReciente = true ac.log("accidente a más de 180") activarVSC(0) else accidenteReciente = false end -- Verificar si ha habido un accidente reciente a 50kmh y si justo después se gira mi coche, sacamos el vsc if collisionDepthChange > 0.09 and car.collidedWith ~= 0 and car.speedKmh > 50 then accidenteReciente = true ac.log("accidente a más de 50kmh me pueden girar o no y si lo hacen, sale el vsc") ac.log(("collisondepth:")..collisionDepthChange) activarVSC(0) else accidenteReciente = false end -- Actualizar los valores previos para la próxima verificación prevCollisionDepth[1] = car.collisionDepth if muestra1 and tiempoTranscurrido >= intervaloMinimo then local muestra2 = lookActual.z -- Redondear los valores a cuatro decimales local muestra1Redondeada = math.floor((muestra1) * 10 + 0.0) local muestra2Redondeada = math.floor((muestra2) * 10 + 0.0) --ac.log("Giro normal: x de " .. tostring(muestra1Redondeada) .. " a " .. tostring(muestra2Redondeada)) -- Comprobar si los números son iguales pero de signo opuesto if muestra1Redondeada == -muestra2Redondeada then ac.log("Giro brusco detectado,(sacamos vsc porque hay accidente reciente): z de " .. tostring(muestra1Redondeada) .. " a " .. tostring(muestra2Redondeada)) if accidenteReciente then reproducirAudio('situacionpeligro.mp3',5) setTimeout(function() activarVSC(0) accidenteReciente = false end, 4) -- El retraso está en segundos end end -- Reinicia la muestra para la próxima comprobación muestra1 = lookActual.z ultimoTiempo = tiempoActual elseif not muestra1 then -- Inicializa la primera muestra y el tiempo si es la primera ejecución muestra1 = lookActual.z ultimoTiempo = tiempoActual end end function reproducirFraseAleatoria() local fraseAleatoria = frases[math.random(#frases)] agregarMensajeACola(fraseAleatoria) -- Aquí puedes agregar el código para reproducir la frase como audio si lo deseas end function script.update(dt) if ac.getSim().isSessionStarted then detectarGiroBruscoYActivarVSC()-- Sistema de detección de giros bruscos roscos detectarVuelcos() detectarCortesYDT() -- -- Ejemplo de cómo usar la función para reproducir la temperatura del agua como audio. -- if ac.getCar(0).waterTemperature then -- local temperaturaAgua = tostring(math.floor(ac.getCar(0).waterTemperature)) -- reproducirNumeroComoAudio(temperaturaAgua) -- ac.log("temperatura agua = " .. temperaturaAgua) -- end ----reproducir frases cada minuto ultimaReproduccion = ultimaReproduccion + dt if ultimaReproduccion >= tiempoEntreReproduccion then reproducirFraseAleatoria() ultimaReproduccion = 0 end end if sessionRestarted == true then reiniciarEstado() sessionRestarted = false ac.log("DETECTADO REINCIO") end sesionActual = ac.getSim().raceSessionType -- Comprobar si la sesión ha cambiado if sesionActual ~= sesionAnterior then -- La sesión ha cambiado reiniciarEstado() sesionAnterior = sesionActual -- Actualizar sesionAnterior end -- Read Current Flag (Update only on change) if activeFlag ~= ac.getSim().raceFlagType then activeFlag = ac.getSim().raceFlagType ac.log("Current Flag = " .. activeFlag) end -- ac.log("temperatura de agua:" .. ac.getCar(0).waterTemperature) -- ac.log("vida del motor:" .. ac.getCar(0).engineLifeLeft) -- ac.log("preloader diff:" .. ac.getCar(1).differentialPreload) -- ac.log("coast diff:" .. ac.getCar(1).differentialCoast) -- ac.log("diff power:" .. ac.getCar(1).differentialPower) --A PARTIR DE AQUI, EL CÓDIGO NORMAL QUE TENÍA, AHORA SOLO SE EJECUTA EN SESIÓN RACE if ac.getSim().raceSessionType == ac.SessionType.Race then --ACTUALIZAR MENSAJES EN EL CUADRO actualizarYMostrarMensaje(dt) actualizarEstadoDelJuego(dt)--esta función avisa de que es la última vuelta de la ventana de pits o expusion --actualizarYDibujarBarraVSC(dt) --esto es para las averías tiempoUltimaComprobacion = tiempoUltimaComprobacion + dt if tiempoUltimaComprobacion >= intervaloComprobacion then comprobarRoturaCoche() tiempoUltimaComprobacion = 0 -- Reinicia el contador después de cada comprobación end -- --local car = ac.getCar(0) -- Obtener el coche del jugador (índice 0) -- --local currentPenalty = car.currentPenaltyType -- Obtener el tipo de penalización actual del coche del jugador -- --local currentPenalty = ac.getCar(0).currentPenaltyType -- ac.log("currentpenalty: " .. (ac.getCar(0).currentPenaltyType)) -- ac.log("Tipo de penalización actual: " .. tostring(ac.getCar(0).currentPenaltyType)) -- if ac.getCar(0).currentPenaltyType == ac.PenaltyType.SlowDown then -- Suponiendo que SlowDown es el valor correcto -- physics.setCarPenalty(ac.PenaltyType.None, -1) -- Suponiendo que 'None' elimina la penalización -- ac.log("currentpenalty: " .. (ac.getCar(0).currentPenaltyParameter)) -- ac.log("Tipo de penalización actual: " .. tostring(ac.getCar(0).currentPenaltyType)) -- end -- Obtiene el coche del usuario local userCar = ac.getCar(0) -- Verifica si hay una penalización activa que no sea 'None' if userCar.currentPenaltyType ~= ac.PenaltyType.None then -- Si hay una penalización diferente de 'None', registra el tipo y el parámetro de la penalización --ac.log("Penalización activa: Tipo = " .. tostring(userCar.currentPenaltyType) .. ", Parámetro = " .. tostring(userCar.currentPenaltyParameter)) end --ac.log("currentpenalty: " .. (currentPenalty)) -- if currentPenalty == ac.PenaltyType.SlowDown then -- -- Si el coche del jugador tiene la penalización SlowDown, establecer la penalización a None -- physics.setCarPenalty(ac.PenaltyType.None, -1, 0) -- ac.getCarState(0).currentPenaltyType.None -- ac.log("SLOW DOWN ANULADO") -- end -- Actualización del temporizador if settings.timerStarted then settings.timer = settings.timer + dt end -- Configurar IA después de 15 segundos desde el inicio de la carrera if settings.timerStarted and settings.timer > 15 and not settings.initialized then settings.initialized = true settings.autopilot.Speed = 100 -- Configura el nivel de IA al x% settings.autopilot.enabled = true -- Asegúrate de que el autopiloto esté habilitado settings.autopilot.Agression = 50 for i = 1, 23 do -- Itera sobre todos los coches physics.setAILevel(i, settings.autopilot.Speed / 100) physics.setAIAggression(i, settings.autopilot.Agression / 100) end ac.log("level y agression seteados") ac.log("LEVEL: " .. (settings.autopilot.Speed / 100) .. " AGRSN: " .. (settings.autopilot.Agression / 100)) end ----------------------------------------------------------------------------verificarGiroYActivarVSC(dt)--esto es para ver si nos giran el coche --esto es para el vsc --verificarYActivarVSC() --la comento, ya no la uso porque ahora lo detecto desde la función que detecta giros bruscos verificarmurosYActivarVSC()--verificamos si me doy contra el muro y saco vsc if isVSCActive then actualizarTemporizadorVSC(dt) local mensaje = "" vscTimer = vscTimer + dt -- Actualiza el temporizador con el deltaTime -- Lógica para contar los coches en pits y ajustar la posición local cochesDelanteEnPits = 0 for i = 1, (conteoIAs-1) do local car = ac.getCar(i) if car.isInPitlane and posicionesEnVSC[i] < playerPositionAtVSC then cochesDelanteEnPits = cochesDelanteEnPits + 1 end end local posicionAjustadaDelJugador = playerPositionAtVSC - cochesDelanteEnPits if posicionAjustadaDelJugador < mejorPosicionObjetivoVSC then mejorPosicionObjetivoVSC = posicionAjustadaDelJugador -- Actualiza si el jugador mejora su posición end local tiempoRestante = gracePeriod - vscTimer --ac.log("tiemporestante" .. tostring(tiempoRestante)) if isVSCActive then mensaje = string.format("VSC: MANTÉN LA POSICIÓN %d. VIRTUAL SAFETY CAR 100 KMH/MAX. Tiempo restante: %.2f segundos.", mejorPosicionObjetivoVSC, tiempoRestante) else mensaje = string.format("VSC TERMINADO") imagenamostrar="ingeniero.png" end -- Comprueba si el tiempo restante es igual a 3, 2 o 1 y reproduce el audio correspondiente if tiempoRestante <= 7 and tiempoRestante > 6 then -- Reproduce el audio para 3 segundos reproducirAudio('tres.mp3',1) elseif tiempoRestante <= 5 and tiempoRestante > 4 then -- Reproduce el audio para 2 segundos reproducirAudio('dos.mp3',1) elseif tiempoRestante <= 3 and tiempoRestante > 2 then -- Reproduce el audio para 1 segundo reproducirAudio('uno.mp3',1) elseif tiempoRestante <= 1 and tiempoRestante > 0 then -- Reproduce el audio para 0 segundos reproducirAudio('cero.mp3',1) reproducirAudio('scfin.mp3', 6) end -- Resto del código para mensajes de audio y agregar mensaje a cola agregarMensajeACola(mensaje) imagenamostrar="sc.gif" mensajeActualParaPiloto = mensaje else --SE HA TERMINADO EL VSC --local mensaje = string.format("VSC: fin") --agregarMensajeACola(mensaje) --limpiarColaDeMensajes() --imagenamostrar="ingeniero.png" --reproducirAudio('scfin.mp3', 6) --local mensajefinsc = string.format("VSC: VIRTUAL SAFETY CAR TERMINADO. SE RELANZA LA CARRERA") --mensajeActualParaPiloto = mensajefinsc --mensajeActualParaPiloto = mensaje --agregarMensajeACola(mensaje) end -- Actualización de prevCollisionDepth y prevHitValue para cada coche local carsCount = ac.getSim().carsCount for i = 0, carsCount - 1 do local car = ac.getCar(i) prevCollisionDepth[i+1] = car.collisionDepth -- Asumiendo que tienes una forma de calcular HitValue basado en alguna lógica de colisión -- prevHitValue[i+1] = Aquí deberías calcular o actualizar el HitValue basado en las colisiones detectadas para el coche i end if not isVSCActive then if mostrarMensajeVueltaObligatoria then mensajeActualParaPiloto = mensajeActualParaPilotocierrepits agregarMensajeACola(mensajeActualParaPiloto) else -- Obtener el nombre largo del compuesto de neumáticos al que debe cambiar el jugador local tyreChangeName = ac.getTyresName(0, ruedaCambio) if not tyreChangeName then tyreChangeName = "desconocido" -- En caso de que no se pueda obtener el nombre, usar un valor por defecto end local nombreNeumaticosACambiar = ac.getTyresName(0, indiceNeumaticosACambiar) if not cocheRoto then imagenamostrar="ingeniero.png" end --a continuación hago que el mensaje solo salga si hay ventana de pits local vueltaActual = ac.getCar(0).lapCount + 1 -- Obtiene la vuelta actual del jugador y suma 1 porque lapCount comienza en 0 -- Verifica si la vuelta actual está dentro de alguna ventana de pits abierta if vueltaActual >= vueltaObligatoriaCambioNeumaticos - 1 and vueltaActual <= vueltaObligatoriaCambioNeumaticos + 1 then -- El mensaje se mostrará solo si la vuelta actual está dentro de la ventana permitida if not isVSCActive then if mostrarMensajeVueltaObligatoria then mensajeActualParaPiloto = mensajeActualParaPilotocierrepits else -- Código para preparar y mostrar el mensaje sobre el cambio de neumáticos -- Este bloque es donde preparas el mensaje como ya estabas haciendo end mensajeActualParaPiloto = "Recuerda: Puedes entrar a pits entre las vueltas " .. (vueltaObligatoriaCambioNeumaticos - 1) .. " y " .. (vueltaObligatoriaCambioNeumaticos + 1) .. " Debes montar neumaticos " .. nombreNeumaticosACambiar .. "." agregarMensajeACola(mensajeActualParaPiloto) -- Asegúrate de agregar el mensaje a la cola solo una vez reproducirAudio('abrepits.mp3',3) end else -- Si no está en la ventana de pits, asegúrate de no mostrar el mensaje mostrarMensajeVueltaObligatoria = false end --imagenamostrar="ingeniero.png" end end if cocheRoto then --ac.log("averia") mensajeActualParaPiloto = mensajedeaveria agregarMensajeACola(mensajeActualParaPiloto) ac.setFFBMultiplier(3) mostrarVentanaAveria("AVERÍA IMPORTANTE - ENTRA EN BOXES INMEDIATAMENTE") --ui.image('averia_0005_005-piston.png', vec2(85, 85)) -- Establece un temporizador para cambiar cocheRoto a false después de 10 segundos setTimeout(function() cocheRoto = false ac.setFFBMultiplier(1) -- Aquí también podrías añadir cualquier otra lógica que necesites ejecutar -- después de que el estado de cocheRoto cambie, como registrar un mensaje. --ac.log("El coche ha sido reparado.") end, 15) -- El retraso está en segundos end if avisoUltimaOportunidadEmitido then --ac.log("cierre de ventana de pits") setTimeout(function() mensajeActualParaPiloto = mensajeActualParaPilotocierrepits agregarMensajeACola(mensajeActualParaPiloto) imagenamostrar="ingeniero.png" end, 15) -- El retraso está en segundos end --PONEMOS FUEL A TODOS aiDriverCount = ac.getSim().carsCount - 1 --if ac.getSim().raceSessionType == ac.SessionType.Race and ac.getPatchVersionCode() >= 2278 then if ac.getSim().raceSessionType == ac.SessionType.Race then if ac.getCar(0).speedMs < 1 and ac.getCar(0).lapCount < 1 then -- used to run only at first lap while stationary. if aiTakePlayersFuel == false then physics.setCarFuel(0, ac.getCar(1).fuel) -- Set player fuel to same as ai opponents --ac.log("Set players fuel level to: " .. ac.getCar(1).fuel) elseif aiTakePlayersFuel == true then -- Loop through ai drivers for i = 1, aiDriverCount, 1 do physics.setCarFuel(i, ac.getCar(0).fuel) -- Set ai fuel to the same as the players fuel --ac.log("Set ai driver's fuel to: " .. ac.getCar(i).fuel) end end end else ac.log("Not in race session or CSP version too low. Must be atleast v0.2.0") end -- AQUÍ COMPRUEBO QUE LAS RUEDAS DEL PILOTO SEAN LAS QUE HE INDICADO AL INICIO. -- Solo ejecutar la comprobación si no se ha realizado antes if not neumaticosVerificados then local velocidadActual = ac.getCarSpeedKmh(0) -- Obtener la velocidad actual del coche del jugador en km/h -- Verificar si el coche del jugador ha alcanzado la velocidad mínima requerida if velocidadActual > velocidadParaVerificarNeumaticos then -- Obtener el nombre corto del compuesto de neumáticos actualmente seleccionado para el coche del jugador local nombreCortoNeumaticos = ac.getTyresName(0, -1) local carIndex = 0 -- Índice del coche actual (0 para el jugador) --LISTO LOS NEUMÁTICOS DISPONIBLES PARA EL COCHE ELEGIDO local carIndex = 0 -- Índice del coche actual (0 para el jugador) local nombreCortoNeumaticos = ac.getTyresName(carIndex, -1) local indiceNeumaticosSeleccionado = ac.getTyresIndex(carIndex, nombreCortoNeumaticos) ac.log("El índice del juego de neumáticos actualmente seleccionado es: " .. indiceNeumaticosSeleccionado) ac.log("Listando el índice seleccionado y 3 índices menos y 3 índices más:") -- Función para obtener el nombre largo de un índice de neumático (o una cadena vacía si no existe) local function getNombreLargoNeumatico(indice) local nombreLargo = ac.getTyresLongName(carIndex, indice) or "" return nombreLargo end -- Listar los 3 índices menores for i = indiceNeumaticosSeleccionado - 1, indiceNeumaticosSeleccionado - 3, -1 do if i >= 0 then local nombreLargo = getNombreLargoNeumatico(i) ac.log(i .. ". " .. nombreLargo .. " (Índice: " .. i .. ")") end end -- Listar el índice seleccionado local nombreLargoSeleccionado = getNombreLargoNeumatico(indiceNeumaticosSeleccionado) ac.log(indiceNeumaticosSeleccionado .. ". " .. nombreLargoSeleccionado .. " (Índice: " .. indiceNeumaticosSeleccionado .. ")") -- Listar los 3 índices mayores for i = indiceNeumaticosSeleccionado + 1, indiceNeumaticosSeleccionado + 3 do local nombreLargo = getNombreLargoNeumatico(i) if nombreLargo ~= "" then ac.log(i .. ". " .. nombreLargo .. " (Índice: " .. i .. ")") end end ----------------------------------- if nombreCortoNeumaticos then local indiceNeumaticos = ac.getTyresIndex(0, nombreCortoNeumaticos) ac.log("El índice del juego de neumáticos actualmente seleccionado es: " .. indiceNeumaticos) -- Comprobar si el índice del juego de neumáticos coincide con el neumático de inicio requerido if indiceNeumaticos ~= indiceNeumaticosInicio then -- Si el índice no coincide, el jugador no ha puesto el neumático indicado. Expulsar al jugador. ac.log("El jugador no ha seleccionado el compuesto de neumáticos correcto. Se cerrará el juego.") imagenamostrar="ingeniero.png" mensajeActualParaPiloto = "DESCALIFICADO POR NO MONTAR EL COMPUESTO INDICADO POR EL EQUIPO." agregarMensajeACola(mensajeActualParaPiloto) ac.setSystemMessage("Penalización", "Has sido penalizado por no montar el compuesto de neumáticos indicado por el equipo.") physics.setCarPenalty(ac.PenaltyType.BlackFlag, -1) else ac.log("El jugador ha seleccionado el compuesto de neumáticos correcto.") end -- Marcar como verificado para evitar comprobaciones repetidas neumaticosVerificados = true else ac.log("No se pudo obtener el nombre corto del juego de neumáticos. No es posible verificar el compuesto.") end end end -- Cosas a hacer una sola vez al inicio if primeraVez == true then conteoIAs = ac.getSim().carsCount - 1 -- Restamos uno aquí porque el conteo incluye al jugador. math.randomseed(os.time()) -- usado para que los números aleatorios funcionen correctamente. asignarNeumaticosInicialesIA() definirTiemposParadaEnPitsParaCambioNeumaticosIA() establecerEstrategiasParadaIAs() --reproducirFraseAleatoria() primeraVez = false --ac.getSession(ac.getSim().timeHours(23)) ac.getSession(ac.getSim().currentSessionIndex) --ac.log(ac.getSim().timeHours)--SABER LA HORA --aqui pongo una ventana modalPopup para informar de la rueda que hay que montar en la salida -- Obtiene el nombre corto del compuesto de neumáticos con el que el jugador debe comenzar local nombreCortoNeumaticos = ac.getTyresName(indiceNeumaticosInicio) local carIndex = 0 -- Índice del coche actual (0 para el jugador) local indiceNeumaticosInicio = 0 -- Asumimos que este es el índice del neumático con el que debes iniciar -- Obtiene el nombre largo del neumático para el índice de inicio local nombreLargoNeumaticoInicio = ac.getTyresLongName(carIndex, indiceNeumaticosInicio) or "neumático desconocido" -- Mensaje para la ventana modal local mensajeNeumaticosInicio = "Recuerda que debes salir con el neumático " .. nombreLargoNeumaticoInicio -- Muestra la ventana modal con el mensaje ui.modalPopup( "Recordatorio de Neumáticos", -- Título de la ventana modal mensajeNeumaticosInicio, -- Mensaje a mostrar, ahora incluye el compuesto de neumáticos dinámico "OK", -- Texto del botón de OK "", -- Texto del botón de Cancelar, `nil` porque quizás no necesitemos uno nil, -- ID del ícono para el botón de OK, `nil` para no usar ícono nil, -- ID del ícono para el botón de Cancelar, `nil` para no usar ícono function(okPressed) if okPressed then -- Acciones a realizar cuando el usuario presiona OK, si es necesario ac.log("El usuario ha presionado OK en el recordatorio del neumático " .. nombreLargoNeumaticoInicio .. ".") end end ) end -- Actualizar array actualizarTiempoDetenidoEnPitsTodasIAs(dt) -- Gestión de la sesión de carrera en vivo gestionarSolicitudesEntradaAPitsIAs() gestionarIAsEnPits() -- A PITS EN VUELTA OBLIGATORIA: Marcar si el jugador ha entrado a pits en la vuelta obligatoria o en las vueltas adyacentes if (ac.getCar(0).lapCount + 1 >= vueltaObligatoriaCambioNeumaticos - 1) and (ac.getCar(0).lapCount + 1 <= vueltaObligatoriaCambioNeumaticos + 1) and ac.getCar(0).isInPitlane then jugadorEntroAPitsEnVueltaObligatoria = true end -- COMPROBAR GOMAS INDICADAS DESPUÉS DE LA PARADA EN PITS if ac.getCar(0).lapCount + 1 > vueltaObligatoriaCambioNeumaticos and (jugadorEntroAPitsEnVueltaObligatoria or ac.getCar(0).lapCount >= vueltaObligatoriaCambioNeumaticos + 1) then if not ac.getCar(0).isInPitlane then local nombreCortoNeumaticos = ac.getTyresName(0, -1) if nombreCortoNeumaticos then local indiceNeumaticos = ac.getTyresIndex(0, nombreCortoNeumaticos) if indiceNeumaticos == indiceNeumaticosACambiar then if ac.getCar(0).lapCount <= vueltaObligatoriaCambioNeumaticos + 2 then -- Mostrar mensaje y reproducir audio solo si aún no han pasado dos vueltas desde el cambio obligatorio --ac.log("El jugador ha cambiado al compuesto de neumáticos correcto después de la parada en pits.") mensajeActualParaPiloto = "LISTO, NO HAY MÁS PARADAS PROGRAMADAS." imagenamostrar="ingeniero.png" agregarMensajeACola(mensajeActualParaPiloto) if mensajeFinalMostrado == true then reproducirAudio('nomaspits.mp3',5) mensajeFinalMostrado = true end -- Variable global para manejar si el mensaje final ya se mostró if not mensajeFinalMostrado then mensajeFinalMostrado = true end end else -- Si el jugador no cambió al compuesto correcto, manejar como se requiera ac.log("El jugador no ha cambiado al compuesto de neumáticos correcto después de la parada en pits. Debe cambiar a: " .. tostring(indiceNeumaticosACambiar)) -- Verificar si el jugador está después de la última vuelta permitida if ac.getCar(0).lapCount >= vueltaObligatoriaCambioNeumaticos + 1 then -- Aplicar penalización de bandera negra ac.log("El jugador no ha cambiado al compuesto de neumáticos correcto después de la última vuelta permitida.") imagenamostrar="ingeniero.png" mensajeActualParaPiloto = "DESCALIFICADO POR NO MONTAR EL COMPUESTO INDICADO POR EL EQUIPO." agregarMensajeACola(mensajeActualParaPiloto) physics.setCarPenalty(ac.PenaltyType.BlackFlag, -1) ac.setSystemMessage("Penalización", "Has sido penalizado por no cambiar al compuesto de neumáticos indicado.") end end else ac.log("No se pudo obtener el nombre corto del juego de neumáticos. No es posible verificar el compuesto.") end end end -- Verificar si el jugador está en pits y hay una avería activa if ac.getCar(0).isInPitlane and ventanaAveriaActiva and ac.getCar(i).speedKmh < 5 then -- Reparar el coche (restablecer los valores por defecto) physics.setCarEngineLife(0, 1000) -- Vida del motor al 100% physics.setCarRestrictor(0, 0) -- Eliminar restrictor physics.setCarBallast(0, 0) -- Eliminar lastre physics.setCarBodyDamage(0, {0, 0, 0, 0}) -- Eliminar daño en la carrocería physics.setTyresVirtualKM(0, 0, 0) -- Restaurar el desgaste de los neumáticos -- Ocultar la ventana de avería ocultarVentanaAveria() local msg = "VEHICULO REPARADO" local description = "TODO CORRECTO" ac.setSystemMessage(msg, description) ac.setFFBMultiplier(1) -- Restablecer el multiplicador de FFB imagenamostrar="ingeniero - copia.png" reproducirAudio('end.mp3',3) agregarMensajeACola(mensajeActualParaPiloto) ac.setSystemMessage(msg, description) end end--final del if inicial donde miro si es carrera o no lo es. end------FINAL DE FUNCION UPDATE function mostrarVentanaAveria(mensaje) ventanaAveriaActiva = true local msg = "VEHICULO AVERIADO" local description = "! A BOXES YA !" --imagenamostrar="averia.png" --mensajeActualParaPiloto = "VEHICULO AVERIADO ! A BOXES YA ! ! A BOXES YA ! ! A BOXES YA !" agregarMensajeACola(mensajeActualParaPiloto) ac.setSystemMessage(msg, description) ui.toolWindow(windowId, windowPos, windowSize, true, false, function() ui.pushDWriteFont('Segoe UI;Weight=Bold') ui.dwriteText(mensaje, 24) ui.popDWriteFont() -- Aquí puedes agregar una imagen o bandera adicional que indique la urgencia de la reparación ui.image('averia.png', vec2(40, 40)) -- Ejemplo de imagen de bandera roja end) end function ocultarVentanaAveria() ventanaAveriaActiva = false end function actualizarEstadoDelJuego(dt) -- Asume que 'dt' es el delta de tiempo desde la última actualización ultimoTiempoDeActualizacion = ultimoTiempoDeActualizacion + dt -- Verificar la condición de última oportunidad para entrar a pits if not avisoUltimaOportunidadEmitido and (ac.getCar(0).lapCount == vueltaObligatoriaCambioNeumaticos) then if ultimoTiempoDeActualizacion >= intervaloDeActualizacion then mensajeActualParaPilotocierrepits = "OBLIGATORIO: A PITS EN ESTA VUELTA." imagenamostrar="ingeniero.png" agregarMensajeACola(mensajeActualParaPilotocierrepits) reproducirAudio('pitahora.mp3',9) avisoUltimaOportunidadEmitido = true mostrarMensajeVueltaObligatoria = true -- Establece la variable a true ultimoTiempoDeActualizacion = 0 end end -- Asegúrate de restablecer 'avisoUltimaOportunidadEmitido' después de que el jugador entre a pits o cuando sea relevante -- (por ejemplo, si el jugador entra a pits o al iniciar una nueva carrera) end function script.windowMain(dt) ui.image(imagenamostrar, vec2(85, 85)) -- Ajusta (100, 100) al tamaño deseado. if not mensajeFinalMostrado then -- Obtener el nombre largo del compuesto de neumáticos al que debe cambiar el jugador local nombreNeumaticosACambiar = ac.getTyresName(0, indiceNeumaticosACambiar) if not nombreNeumaticosACambiar then nombreNeumaticosACambiar = "desconocido" -- En caso de que no se pueda obtener el nombre, usar un valor por defecto end end ui.pushDWriteFont('Segoe UI;Weight=Bold') ui.sameLine() ui.dwriteText(" " .. string.upper(mensajeActualParaPiloto), 24) ui.popDWriteFont() -- Show Flag Circles if activeFlag == 0 then -- None --ui.drawCircleFilled(vec2(30,38),15,rgbm(0,0,0,1),32) limpiarColaDeMensajes() elseif activeFlag == 1 then -- Green --ui.drawRectFilled(vec2(30,38),100,rgbm(0,1,0,1),62) --ui.drawRectFilled(0,1110, rgbm(0,1,0,1),0, 0) elseif activeFlag == 2 then -- Yellow --ui.drawCircleFilled(vec2(30,38),15,rgbm(1,1,0,1),32) mensajeActualParaPiloto = "" agregarMensajeACola(mensajeActualParaPiloto) ui.drawRectFilled(0,4110, rgbm(1,1,0,1),0, 0) elseif activeFlag == 12 then -- Blue --ui.drawCircleFilled(vec2(30,38),15,rgbm(0,0,1,1),32) mensajeActualParaPiloto = "" agregarMensajeACola(mensajeActualParaPiloto) ui.drawRectFilled(0,4110, rgbm(0,0,1,1),0, 0) elseif activeFlag == 13 then -- Checkered --ui.drawCircleFilled(vec2(30,38),15,rgbm(1,1,1,1),32) elseif activeFlag == 13 then -- Checkered --ui.drawCircleFilled(vec2(30,38),15,rgbm(1,1,1,1),32) else --ui.drawCircleFilled(vec2(30,38),15,rgbm(0,0,0,0.7),32) end -- -- ID único para la ventana -- local windowId = "NumeroDeCortesDeVuelta" -- -- Posición de la ventana (ajusta según tus necesidades) -- local windowPos = vec2(50, 100) -- Ejemplo: en la parte superior derecha -- -- Tamaño de la ventana (ajusta según tus necesidades) -- local windowSize = vec2(1920, 100) -- Suficiente para mostrar el mensaje -- ui.transparentWindow(windowId, windowPos, windowSize, true, false, function() -- --ui.image(imagenamostrar, vec2(85, 85)) -- -- Asumiendo que 'lapCutsCount' es la variable que tiene el número actual de cortes -- local mensaje = "direccion: " .. ac.getCar(0).compass -- -- Configura el estilo de la fuente si es necesario -- ui.pushDWriteFont('Segoe UI;Weight=Bold') -- -- Muestra el mensaje. Ajusta el tamaño y posición según sea necesario -- ui.dwriteText(mensaje, 24) -- --ui.dwriteText(" " .. string.upper(mensajeActualParaPiloto), 24) -- ui.popDWriteFont() -- end) end -- Runs once per frame on to update Window Icon --------------------------------------------REPRODUCCION DE AUDIOS EN COLA-------------------------------------------------- -- La función de temporizador para manejar la cola de reproducción. local function temporizadorReproduccion(nombreArchivo, reproductor) setTimeout(function() audiosReproducidos[nombreArchivo] = nil -- Aquí puedes llamar a la función que verifica si hay más audios en la cola y reproduce el siguiente. reproducirSiguiente() end, reproductor:getLength()) -- Utiliza la duración del archivo de audio si es posible obtenerla. end -- La función para reproducir el siguiente audio en la cola. function reproducirSiguiente() -- Verifica si hay audios en la cola y no se está reproduciendo nada actualmente. if not reproduciendo and #colaReproduccion > 0 then -- Obtiene el siguiente archivo de audio de la cola. local nombreArchivo = table.remove(colaReproduccion, 1) local reproductor = ui.MediaPlayer(ac.getFolder(ac.FolderID.ScriptOrigin) .. '/' .. nombreArchivo) reproductor:setVolume(0.5) -- Ajusta el volumen. reproductor:play() -- Inicia la reproducción. audiosReproducidos[nombreArchivo] = true -- Marca el audio como reproducido. -- Configura el temporizador basado en la duración estimada del audio. temporizadorReproduccion(nombreArchivo, reproductor) end end -- La función para añadir un audio a la cola y reproducir si es posible. function reproducirAudio(nombreArchivo, duracion) -- Verifica si el archivo actual es diferente al último reproducido if nombreArchivo ~= ultimoAudioReproducido then if audioActual == nil then -- No hay ningún audio reproduciéndose actualmente, así que reproduce el archivo local reproductor = ui.MediaPlayer(ac.getFolder(ac.FolderID.ScriptOrigin) .. '/' .. nombreArchivo) reproductor:setVolume(0.5) reproductor:play() audioActual = nombreArchivo ultimoAudioReproducido = nombreArchivo -- Actualiza el último audio reproducido -- Establece un temporizador para marcar el audio como no reproducido después de que finalice setTimeout(function() audioActual = nil end, duracion) end end end function reproducirNumeroComoAudio(numero) -- Convertir el número a cadena para iterar sobre cada carácter local numeroComoCadena = tostring(numero) -- Redondear el número entero hacia abajo para identificar el archivo de la carpeta específica local parteEntera = math.floor(numero) local rutaArchivoEntero = "numbers/" .. tostring(parteEntera) .. "/1.wav" -- Reproducir la parte entera del número reproducirAudio(rutaArchivoEntero, 0) -- Asumiendo que cada audio dura 1 segundo -- Verificar si hay parte decimal if numeroComoCadena:find('%.') then -- Reproducir el sonido para el punto decimal reproducirAudio("numbers/point/1.wav", 0) -- Iterar sobre la parte decimal del número local inicioDecimal = numeroComoCadena:find('%.') + 1 for i = inicioDecimal, #numeroComoCadena do local caracter = numeroComoCadena:sub(i, i) local rutaArchivoDecimal = "numbers/" .. caracter .. "/1.wav" -- Reproducir cada dígito de la parte decimal reproducirAudio(rutaArchivoDecimal, 0) end end end -- function asignarNeumaticosInicialesIA() -- if not neumaticosAsignados then -- for i = 1, conteoIAs do -- -- Asignar un compuesto de neumáticos aleatorio entre 0 y 1 para cada IA. -- local indiceNeumaticosAleatorio = math.random(0, 1) -- physics.setAITyres(i, indiceNeumaticosAleatorio) -- -- Almacenar el compuesto de neumáticos inicial en la tabla compuestosSalidaIA. -- compuestosSalidaIA[i] = indiceNeumaticosAleatorio -- --ac.log("Coche: " .. i .. " set starting tyre index: " .. indiceNeumaticosAleatorio) -- end -- neumaticosAsignados = true -- Establecer la variable de control a true después de la asignación. -- end -- end function asignarNeumaticosInicialesIA() if not neumaticosAsignados then local conteoNeumaticos = {} local nombresNeumaticos = {} for i = 1, conteoIAs do local indiceNeumaticosAleatorio = math.random(0, 1) physics.setAITyres(i, indiceNeumaticosAleatorio) compuestosSalidaIA[i] = indiceNeumaticosAleatorio conteoNeumaticos[indiceNeumaticosAleatorio] = (conteoNeumaticos[indiceNeumaticosAleatorio] or 0) + 1 if not nombresNeumaticos[indiceNeumaticosAleatorio] then nombresNeumaticos[indiceNeumaticosAleatorio] = ac.getTyresLongName(0, indiceNeumaticosAleatorio) end end neumaticosAsignados = true local partesMensaje = {} for indiceNeumatico, cantidad in pairs(conteoNeumaticos) do if cantidad > 0 then table.insert(partesMensaje, "" .. cantidad .. " rivales salen con neumaticos " .. nombresNeumaticos[indiceNeumatico]) end end local mensajeActualParaPiloto = table.concat(partesMensaje, " y ") .. "." agregarMensajeACola(mensajeActualParaPiloto) end end function establecerEstrategiasParadaIAs() -- Define las estrategias de pits para la IA for i = 1, conteoIAs, 1 do estrategiasParadaIAs[i] = math.random(vueltaObligatoriaCambioNeumaticos - 1, vueltaObligatoriaCambioNeumaticos + 1) -- Cada IA entrará en pits en una vuelta aleatoria entre 2 y 4 end end function gestionarSolicitudesEntradaAPitsIAs() -- Gestiona las solicitudes de entrada a pits de la IA for i = 1, conteoIAs, 1 do if (ac.getCar(i).lapCount + 1) == estrategiasParadaIAs[i] and ac.getCar(i).lapTimeMs < 1000 and ac.getCar(i).speedKmh > 1 then -- 1s = 1000ms physics.setAIPitStopRequest(i, true) --ac.log("Coche: " .. i .. " requests a pit") end end end function definirTiemposParadaEnPitsParaCambioNeumaticosIA() -- Define los tiempos de parada en pits para cada IA for i = 1, conteoIAs, 1 do --tiempoParadaEnPitsParaCambioNeumaticosIA[i] = tiempoEsperadoCambioNeumaticos tiempoDetenidoEnPitsIA[i] =tiempoEsperadoCambioNeumaticos end end function actualizarTiempoDetenidoEnPitsTodasIAs(dt) -- Actualiza el tiempo que cada conductor permanece detenido en pits for i = 1, conteoIAs, 1 do if ac.getCar(i).isInPitlane == true and ac.getCar(i).speedKmh < 5 then tiempoDetenidoEnPitsIA[i] = tiempoDetenidoEnPitsIA[i] + dt else if ac.getCar(i).isInPitlane == false and tiempoDetenidoEnPitsIA[i] == nil then tiempoDetenidoEnPitsIA[i] = 0 end end end end -- function gestionarIAsEnPits() ---PARECE FUNCIONAR CON CSP 2.0 PERO CREO QUE CON 2.1 NO FUNCIONABA -- -- Controla el comportamiento de la IA en pits -- for i = 1, conteoIAs, 1 do -- if ac.getCar(i).isInPit then -- -- Determinar el neumático opuesto al compuesto de salida -- local neumaticoSalida = compuestosSalidaIA[i] -- local nuevoIndiceNeumaticos = (neumaticoSalida == 0) and 1 or 0 -- Cambia al otro tipo de neumático. -- physics.setAITyres(i, nuevoIndiceNeumaticos) -- --ac.log("Coche: " .. i .. " changed to tyre index: " .. nuevoIndiceNeumaticos) -- physics.setGentleStop(i, false) -- Reanuda el movimiento de la IA después del cambio de neumáticos. -- end -- end -- end function gestionarIAsEnPits() -- Controla el comportamiento de la IA en pits for i = 1, conteoIAs, 1 do if ac.getCar(i).isInPit then -- Verificar si el tiempo detenido en pits es suficiente if tiempoDetenidoEnPitsIA[i] >= tiempoEsperadoCambioNeumaticos then -- Determinar el neumático opuesto al compuesto de salida local neumaticoSalida = compuestosSalidaIA[i] local nuevoIndiceNeumaticos = (neumaticoSalida == 0) and 1 or 0 -- Cambia al otro tipo de neumático. physics.setAITyres(i, nuevoIndiceNeumaticos) --ac.log("Coche: " .. i .. " changed to tyre index: " .. nuevoIndiceNeumaticos) physics.setGentleStop(i, false) -- Reanuda el movimiento de la IA después del cambio de neumáticos. tiempoDetenidoEnPitsIA[i] = 0 -- Reinicia el tiempo detenido en pits después del cambio de neumáticos end end end end function comprobarRoturaCoche() if not cocheRoto and math.random(1, 100) <= 3 then --porcentaje de probabilidad de averia cocheRoto = true local averia = math.random(1, 10) -- Selecciona una avería al azar de la lista mensajeActualParaPiloto = "" agregarMensajeACola(mensajeActualParaPiloto) if averia == 1 then physics.setCarEngineLife(0, 600) -- Deja el motor al 60% de su vida útil. imagenamostrar = "averia_0005_005-piston.png" mensajeActualParaPiloto = "PROBLEMA GRAVE: Sobrecalentamiento del motor detectado, motor al 60%. Entra en pits a reparar." reproducirAudio('danosleves.mp3',7) elseif averia == 2 then physics.setCarRestrictor(0, 50) -- Aplica un restrictor del 50%. imagenamostrar = "averia_0000_010-turbo-engine.png" mensajeActualParaPiloto = "ALERTA: Fallo en el sistema de turbo detectado. Motor al 50%. Necesitamos reparaciones urgentes." reproducirAudio('danosgraves.mp3',10) elseif averia == 3 then physics.setCarBallast(0, 1500) -- Añade lastre. imagenamostrar = "averia_0007_003-gears.png" mensajeActualParaPiloto = "ATENCIÓN: Problemas en la caja de cambios. Debemos reparar. Por favor, regresa a boxes." reproducirAudio('danosgraves.mp3',10) elseif averia == 4 then physics.setCarBodyDamage(0, {45, 45, 0, 0}) -- Daño frontal y trasero. imagenamostrar = "averia_0008_002-engine-oil.png" mensajeActualParaPiloto = "AVISO: Detectada una fuga de líquido. Debe haber fallos mecánicos. Deberíamos revisar el coche." reproducirAudio('danosleves.mp3',7) elseif averia == 5 then physics.setCarRestrictor(0, 60) -- Aplica un restrictor del 20%. imagenamostrar = "averia_0009_001-brake.png" mensajeActualParaPiloto = "URGENTE: Fallos en el sistema de frenos. La electrónica del coche está en estado de protección, regresa a boxes." reproducirAudio('danosgraves.mp3',8) elseif averia == 6 then physics.setTyresVirtualKM(0, 60, 500) -- Desgaste acelerado de neumáticos. imagenamostrar = "averia_0004_006-fan.png" mensajeActualParaPiloto = "PROBLEMA: Neumático reventado. Tu carrera ha terminado." reproducirAudio('danosseveros.mp3',8) elseif averia == 7 then physics.setCarBodyDamage(0, {0, 0, 50, 50}) -- Daño lateral. imagenamostrar = "averia_0003_007-oil.png" mensajeActualParaPiloto = "NOTIFICACIÓN: Desgaste de neumáticos anormalmente rápido. Daños significativos en gomas. Considera un cambio de neumáticos." reproducirAudio('danosleves.mp3',10) elseif averia == 8 then physics.setCarEngineLife(0, 980) -- Motor al 98% de su capacidad. imagenamostrar = "averia_0002_008-battery.png" mensajeActualParaPiloto = "ADVERTENCIA: Batería baja o fallos de arranque. Vida del motor al 98%. Es posible que necesites asistencia." reproducirAudio('danosgraves.mp3',7) elseif averia == 9 then physics.setCarBallast(0, 900) -- Añade 500 kg de lastre. imagenamostrar = "averia_0006_004-chain.png" mensajeActualParaPiloto = "PRECAUCIÓN: Pérdida de potencia detectada. Podría haber un problema serio." reproducirAudio('danosgraves.mp3',10) elseif averia == 10 then physics.setCarRestrictor(0, 60) -- Aplica un restrictor del 60%. imagenamostrar = "averia_0005_005-piston.png" mensajeActualParaPiloto = "ALERTA MÁXIMA: Sistema de escape dañado. Esto afecta directamente al rendimiento." reproducirAudio('danosgraves.mp3',8) end agregarMensajeACola(mensajeActualParaPiloto) mensajedeaveria = mensajeActualParaPiloto mostrarVentanaAveria() end end -------COMIENZO DE LAS FUNCIONES DEL VSC -- Función para actualizar el temporizador de VSC function actualizarTemporizadorVSC(dt) if isVSCActive and vscTimerActive then vscTimer = vscTimer + dt if vscTimer >= gracePeriod then -- Antes de finalizar el VSC, verifica si el jugador adelantó verificarPosicionJugadorYAplicarPenalizacion() -- Finaliza el VSC finalizarVSC() end end end -- Función para verificar la posición del jugador y aplicar penalización si adelantó durante el VSC function verificarPosicionJugadorYAplicarPenalizacion() local posicionActual = ac.getCar(0).racePosition -- Usamos mejorPosicionObjetivoVSC para verificar si el jugador adelantó ilegalmente if posicionActual < mejorPosicionObjetivoVSC then -- El jugador ha adelantado durante el VSC, aplicar penalización ac.log("El jugador adelantó durante el VSC. Aplicando bandera negra.") agregarMensajeACola("VSC: ADELANTASTE POSICIONES DURANTE EL VSC. BANDERA NEGRA.") physics.setCarPenalty(ac.PenaltyType.BlackFlag, 0) end end -- Función para finalizar el VSC function finalizarVSC() isVSCActive = false vscTimerActive = false vscTimer = 0 -- Opcional, si quieres resetear el temporizador ajustarVelocidadIA(500) -- Restablece la velocidad máxima de IA settings.autopilot.Agression = 50 for i = 0, carsCount - 1 do local car = ac.getCar(i) posicionesEnVSC[i+1] = car.racePosition physics.setAIAggression(i, settings.autopilot.Agression / 100) end reproducirAudio('scfin.mp3', 6) agregarMensajeACola("VSC: VIRTUAL SAFETY CAR FINALIZADO. CARRERA LIBRE. ") end -- Función para ajustar la velocidad máxima de los coches IA, esta función simple funciona bien para mantener a todos en x/kmh function ajustarVelocidadIA(velocidad) local carsCount = ac.getSim().carsCount for i = 0, carsCount - 1 do physics.setAITopSpeed(i, velocidad) end end -- -- Función para detectar colisiones DE TODOS LOS COCHES DE LA PARRILLA y activar el VSC -- function verificarYActivarVSC() -- local carsCount = ac.getSim().carsCount -- --ac.log(ac.getCarByDriverName("VRS-TV")) con esto veo que el index de mi coche es el 0 -- for i = 0, carsCount - 1 do -- local car = ac.getCar(i) -- local collisionDepthChange = car.collisionDepth - (prevCollisionDepth[i+1] or 0) -- local hitValueChange = HitValue - (prevHitValue[i+1] or 0) -- if (collisionDepthChange > 0 or hitValueChange > 0) and car.collidedWith ~= 0 and car.speedKmh > 100 then -- if i ==0 then -- Si es tu coche (índice 0) -- activarVSC(car) -- end -- end -- Actualiza los valores previos para la próxima verificación -- prevCollisionDepth[i+1] = car.collisionDepth -- prevHitValue[i+1] = HitValue -- end -- end -- function verificarmurosYActivarVSC() -- Función para detectar colisiones DE MI PROPIO COCHE contra el muro, y saco vsc -- local carsCount = ac.getSim().carsCount -- --ac.log(ac.getCarByDriverName("VRS-TV")) con esto veo que el index de mi coche es el 0 -- for i = 0, carsCount - 1 do -- local car = ac.getCar(i) -- --local collisionDepthChange = car.collisionDepth - (prevCollisionDepth[i+1] or 0) -- --local hitValueChange = HitValue - (prevHitValue[i+1] or 0) -- if car.collidedWith == 0 then -- if i ==0 then -- Si es tu coche (índice 0) -- ac.log("ostia contra el muro") -- activarVSC(car) -- end -- end -- Actualiza los valores previos para la próxima verificación -- prevCollisionDepth[i+1] = car.collisionDepth -- --prevHitValue[i+1] = HitValue -- end -- end function verificarmurosYActivarVSC() local carsCount = ac.getSim().carsCount for i = 0, carsCount - 1 do local car = ac.getCar(i) if car.collidedWith == 0 then -- Si el coche colisionó con un muro if i == 0 and car.speedKmh > 70 and colisionPreviaConCoche then -- Verifica si es tu coche, si va a más de 70 km/h y si hubo una colisión previa con otro coche ac.log("Impacto contra el muro a más de 70km/h después de una colisión previa con otro coche. Activando VSC.") activarVSC(car) colisionPreviaConCoche = false -- Opcional: Resetear el estado de colisión previa si así lo deseas después de activar el VSC end end -- Actualiza los valores previos para la próxima verificación prevCollisionDepth[i+1] = car.collisionDepth end end -- Función para activar el VSC function activarVSC(car) reproducirAudio('sc.mp3', 9) imagenamostrar="sc.gif" isVSCActive = true vscTimerActive = true vscTimer = 0 -- Reinicia el temporizador --playerPositionAtVSC = ac.getCarLeaderboardPosition(0) playerPositionAtVSC = ac.getCar(0).racePosition ajustarVelocidadIA(velocidadiaenvsc) --ac.log("mantén la posición, VSC de 100 km/h.") isVSCActive = true posicionesEnVSC = {} playerPositionAtVSC = ac.getCar(0).racePosition mejorPosicionObjetivoVSC = playerPositionAtVSC -- Inicializa con la posición actual del jugador local carsCount = ac.getSim().carsCount settings.autopilot.Agression = 1 for i = 0, carsCount - 1 do local car = ac.getCar(i) posicionesEnVSC[i+1] = car.racePosition physics.setAIAggression(i, settings.autopilot.Agression / 100) end end --COLA DE MENSAJES function limpiarColaDeMensajes() colaDeMensajes = {} -- Reinicia la cola de mensajes a una lista vacía end -- Función para agregar mensajes a la cola function agregarMensajeACola(mensaje) -- Verificar si el mensaje ya está en cola for _, msg in ipairs(colaDeMensajes) do if msg == mensaje then return -- Si el mensaje ya está en cola, no hacer nada end end table.insert(colaDeMensajes, mensaje) end -- Función para actualizar y mostrar mensajes function actualizarYMostrarMensaje(dt) if mensajeMostrando and #colaDeMensajes > 0 then tiempoMensaje = tiempoMensaje + dt if tiempoMensaje >= duracionMensaje then -- Tiempo de mostrar el mensaje actual ha terminado, mostrar el siguiente mensaje table.remove(colaDeMensajes, 1) -- Elimina el mensaje mostrado de la cola tiempoMensaje = 0 -- Reinicia el contador de tiempo mensajeMostrando = false end end if not mensajeMostrando and #colaDeMensajes > 0 then -- Hay mensajes en la cola y ninguno se está mostrando mensajeActualParaPiloto = colaDeMensajes[1] -- Toma el primer mensaje de la cola mensajeMostrando = true -- Marca que hay un mensaje mostrándose end end -----------------------------------------------------DETECTAR CAMBIOS DE SESIÓN---------------------------------------------- function reiniciarEstado() -- Reiniciar variables globales ac.log("REINICIANDO TODO") ultimaReproduccion = 0 tiempoEntreReproduccion = 30 vueltaObligatoriaCambioNeumaticos = 3 indiceNeumaticosInicio = 0 indiceNeumaticosACambiar = 1 tiempoEsperadoCambioNeumaticos = 30.8 compuestosSalidaIA = {} aiTakePlayersFuel = false aiDriverCount = 0 velocidadiaenvsc = 100 intervaloComprobacion = 180 activeFlag = nil colisionPreviaConCoche = false tieneDT = false cortesDespuesDeDT = 0 ultimoCorteRegistrado = -1 ventanaAveriaActiva = false windowId = "VentanaAveria" windowPos = vec2(200, 200) windowSize = vec2(400, 200) neumaticosVerificados = false velocidadParaVerificarNeumaticos = 80 mensajeFinalMostrado = false mostrarMensajeFinal = false mensajeActualParaPiloto = "" jugadorEntroAPitsEnVueltaObligatoria = false audiosReproducidos = {} audiosReproducidos = {} audioActual = nil tiempoInicioAudio = 0 duracionAudio = 0 colaReproduccion = {} reproduciendo = false audioAnterior = nil conteoIAs = 0 primeraVez = true estrategiasParadaIAs = {} tiempoParadaEnPitsParaCambioNeumaticosIA = {} tiempoDetenidoEnPitsIA = {} neumaticosInicialesIA = {} neumaticosAsignados = false tiempoUltimaComprobacion = 0 cocheRoto = false mensajedeaveria ="" avisoUltimaOportunidadEmitido = false ultimoTiempoDeActualizacion = 0 intervaloDeActualizacion = 1.0 startTime = nil mostrarMensajeVueltaObligatoria = false ultimoAudioReproducido = nil prevCollisionDepth = 0 prevCollisionDepth = {} prevHitValue = {} HitValue = 0 carsCount = ac.getSim().carsCount for i = 1, carsCount do prevCollisionDepth[i] = 0 prevHitValue[i] = 0 end isVSCActive = false playerPositionAtVSC = nil vscTimer = 0 vscTimerActive = false vscActivationTime = nil gracePeriod = 50 tiempoTranscurridoVSC = 0 imagenamostrar ="ingeniero.png" muestra1 = nil ultimoTiempo = os.clock() accidenteReciente = false posicionesEnVSC = nil playerPositionAtVSC = 0 isVSCActive = false mejorPosicionObjetivoVSC = nil mensajeActualParaPilotocierrepits ="" colaDeMensajes = {} mensajeMostrando = false tiempoMensaje = 3 duracionMensaje = 0.01 settings = { autopilot = {enabled = true, Speed = 100, Agression = 100, Pitnow = false}, initialized = false, timerStarted = false, timer = 0 } settings.timerStarted = true settings.timer = 0 sessionRestarted = false -- Limpiar colas de mensajes limpiarColaDeMensajes() end -----------------------------------------------------DETECTAR CAMBIOS DE SESIÓN---------------------------------------------- local function onSessionStart(sessionIndex, restarted) if restarted then -- Las siguientes líneas se moverían al callback de `ac.startBackgroundWorker` local msg = "Salir del juego" local description = "! Nos vemos pronto !" ac.setSystemMessage(msg, description) end end -- Establecer el callback de inicio de sesión ac.onSessionStart(onSessionStart) --ac.onSessionStart(function(sessionIndex, restarted) ac.log("---------------------------") ac.log("Session Restart occured") sessionRestarted = true; --end)