ceska12

camino al ajuste

Oct 22nd, 2025
577
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Autohotkey 84.53 KB | Software | 0 0
  1. #Requires AutoHotkey v2.0
  2. #SingleInstance Force
  3. #WinActivateForce
  4.  
  5. ; ===== CONFIGURACIÓN ACELERADA =====
  6. SendMode "Input"
  7. SetWorkingDir A_ScriptDir
  8.  
  9. ; ===== DISTRIBUCIÓN POR PERFIL =====
  10. global symbols := Map("cherry", 32, "bell", 26, "lemon", 17, "star", 13, "diamond", 7, "seven", 4, "joker", 1)
  11. global symbolValues := Map("cherry", 2, "bell", 4, "lemon", 7, "star", 12, "diamond", 24, "seven", 48, "joker", 0, "three_jokers", 80)
  12. global spinning := false
  13. global imgPath := "imgs\"
  14. global soundPath := "sounds\"
  15. global spinDuration := 500
  16. global spinInterval := 30
  17. global img1, img2, img3
  18. global lastWin := false
  19. global statsFile := "slot_stats.ini"
  20. global detailedStatsFile := "detailed_stats.ini"
  21. global configFile := "slot_config.ini"
  22. global savedSettingsFile := "AfinacionesGuardadas.ini" ; <<< NUEVA VARIABLE PARA AFINACIONES
  23. global autoSpin := false
  24. global autoSpinCount := 0
  25. global maxAutoSpins := 1000
  26. global lastUpdateCount := 0
  27. global jackpotValue := 100
  28. global jackpotCounter := 0
  29. global testingMode := true  ; ← ACTIVADO PARA PRUEBAS
  30. global DetailedStatsGui := ""
  31. global ReportEdit := ""
  32. global symbolWin := ""
  33.  
  34.  
  35. ; ===== ESTADÍSTICAS DE SISTEMAS DE AYUDA (SIMPLIFICADAS) =====
  36. global pityActivations := 0, pityWins := 0, pityPesos := 0
  37. global multiplierActivationsCount := 0, multiplierWins := 0, multiplierPesosExtra := 0
  38. global jokerActivations := 0, jokerWins := 0, jokerPesos := 0
  39. global jackpotActivations := 0, jackpotWins := 0, jackpotPesos := 0
  40.  
  41. ; ===== NUEVAS VARIABLES PARA ANÁLISIS AVANZADO =====
  42. global symbolFrequency := Map()
  43. global combinationStats := Map()
  44. global volatilityStats := Map()
  45. global rtpBreakdown := Map()
  46.  
  47. ; ===== SISTEMA DE MULTIPLICADORES =====
  48. global multiplierProbability := 25
  49. global multiplierValue := 2.0
  50. global multiplierActivations := 0
  51. global totalMultiplierPesos := 0
  52.  
  53. ; ===== SISTEMA DE PERFILES RTP =====
  54. global RTPProfiles := Map("RTP35", 35.0, "RTP40", 40.0, "RTP45", 45.0, "RTP50", 50.0)
  55. global enableJokerSounds := false
  56. global currentProfile := "RTP40"
  57. global targetRTP := RTPProfiles[currentProfile]
  58.  
  59. ; ===== SISTEMA PITY COUNTER =====
  60. global pityCounter := 0
  61. global pityThreshold := 25
  62. global originalPityThreshold := 20
  63.  
  64. ; ===== SISTEMA BONUS MEJORADO =====
  65. global bonusLights := 0
  66. global maxBonusGiros := 20
  67. global bonusLetters := ["B", "O", "N", "U", "S"]
  68. global letterOrder := []
  69. global bonusThresholds := []
  70.  
  71. ; ===== INICIALIZAR ESTADISTICAS =====
  72. global totalSpins := 0
  73. global totalWins := 0
  74. global totalPesos := 0
  75. global winStats := Map()
  76. global pesosStats := Map()
  77.  
  78. ; ===== ESTADISTICAS DETALLADAS =====
  79. global detailedTotalSpins := 0
  80. global detailedTotalWins := 0
  81. global detailedTotalPesos := 0
  82. global detailedJackpotsWon := 0
  83. global detailedCompensations := 0
  84. global detailedCompensationCount := 0
  85. global detailedWinStats := Map()
  86. global detailedPesosStats := Map()
  87. global detailedJokerRespines := 0
  88. global detailedJokerRespinWins := 0
  89. global detailedJokerRespinPesos := 0
  90.  
  91. ; ===== NUEVAS ESTADISTICAS AVANZADAS =====
  92. global currentLosingStreak := 0
  93. global longestLosingStreak := 0
  94. global symbolWinCounts := Map()
  95. global symbolRTPContributions := Map()
  96. global jokerRTPContribution := 0
  97.  
  98. ; ===== INICIALIZAR LISTA DE SÍMBOLOS =====
  99. global symbolList := []
  100.  
  101. ; ===== VARIABLES RTP MEJORADO =====
  102. global safetyNetThreshold := 40
  103. global safetyNetActive := false
  104. global safetyNetBoost := false
  105. global jokerEffectivenessRTP35 := 35
  106. global jokerEffectivenessRTP40 := 25
  107. global jokerEffectivenessRTP45 := 45
  108. global jokerEffectivenessRTP50 := 65
  109.  
  110. ; ===== SISTEMA DE RESCATE DE VOLATILIDAD MEJORADO =====
  111. global rescueSpinsWithoutWin := 0
  112. global lastRescueSpin := 0
  113. global rescueMinSpinsBetween := 200
  114. global rescueActivations := 0
  115. global rescuePesos := 0
  116. global rescueSymbols := ["lemon", "star", "bell"]  ; Símbolos naturales para rescates
  117. global rescueActive := false  ; Nueva variable para controlar estado
  118.  
  119. ; ===== INTERFAZ GRAFICA =====
  120. MyGui := Gui("+AlwaysOnTop +Resize +OwnDialogs -MaximizeBox", "Tragamonedas JACKPOT 5000 giros")
  121. MyGui.MarginX := 10
  122. MyGui.MarginY := 10
  123. MyGui.SetFont("s12", "Arial")
  124. MyGui.Opt("+OwnDialogs")
  125.  
  126. ; Slot machine frame
  127. MyGui.Add("GroupBox", "w340 h150 Center", "Tragamonedas")
  128. MyGui.Add("Picture", "vSlot1 xp+10 yp+25 w100 h100 Border", imgPath . "question.png")
  129. MyGui.Add("Picture", "vSlot2 x+10 w100 h100 Border", imgPath . "question.png")
  130. MyGui.Add("Picture", "vSlot3 x+10 w100 h100 Border", imgPath . "question.png")
  131.  
  132. ; Mostrar jackpot actual en una línea
  133. MyGui.Add("Text", "xm y+10 w180 Center", "Contador Jackpot:")
  134. JackpotCounterText := MyGui.Add("Text", "x+0 yp w160 Center", "0/5000")
  135.  
  136. ; Logo Joker Giro Gratis - CENTRADO
  137. MyGui.Add("Picture", "vJokerLogo xm+80 y+5 w180 h35", imgPath . "joker_giro_gratis_apagado.png")
  138.  
  139. ; Display de BONUS
  140. MyGui.Add("Picture", "vBonusSlot1 xm+125 y+10 w20 h30", imgPath . "bonus_empty.png")
  141. MyGui.Add("Picture", "vBonusSlot2 x+2 w20 h30", imgPath . "bonus_empty.png")
  142. MyGui.Add("Picture", "vBonusSlot3 x+2 w20 h30", imgPath . "bonus_empty.png")
  143. MyGui.Add("Picture", "vBonusSlot4 x+2 w20 h30", imgPath . "bonus_empty.png")
  144. MyGui.Add("Picture", "vBonusSlot5 x+2 w20 h30", imgPath . "bonus_empty.png")
  145.  
  146. ; Botones de giro en una línea
  147. SpinBtn := MyGui.Add("Button", "xm y+20 w100 h25", "1 Moneda")
  148. SpinBtn.OnEvent("Click", StartSpin)
  149. SpinBtn.Opt("+Default")
  150.  
  151. Spin1000Btn := MyGui.Add("Button", "x+10 w100 h25", "1000")
  152. Spin1000Btn.OnEvent("Click", Start1000Spins)
  153.  
  154. Spin10000Btn := MyGui.Add("Button", "x+10 w100 h25", "10000")
  155. Spin10000Btn.OnEvent("Click", Start10000Spins)
  156.  
  157. ; === NUEVO BOTÓN ULTRA-RÁPIDO ===
  158. UltraTestBtn := MyGui.Add("Button", "xm y+10 w340 h30 cFFFFFF Background009900", "🔥 PRUEBA ULTRA-RÁPIDA 50000")
  159. UltraTestBtn.OnEvent("Click", StartUltraTest)
  160.  
  161. ; === NUEVO BOTÓN EXPORTAR DATOS ===
  162. ExportBtn := MyGui.Add("Button", "xm y+5 w165 h25 cFFFFFF Background3366FF", "📊 EXPORTAR CSV")
  163. ExportBtn.OnEvent("Click", ExportTestData)
  164.  
  165. ; === NUEVO BOTÓN EXPORTAR TXT ===
  166. ExportTxtBtn := MyGui.Add("Button", "x+10 yp w165 h25 cFFFFFF BackgroundFF6600", "📝 EXPORTAR TXT")
  167. ExportTxtBtn.OnEvent("Click", ExportTestResults)
  168.  
  169. ; Boton de seleccion de perfil
  170. ProfileBtn := MyGui.Add("Button", "xm y+10 w340 h30", "Seleccionar Perfil RTP")
  171. ProfileBtn.OnEvent("Click", ShowProfileMenu)
  172.  
  173. ; Indicador de perfil actual
  174. CurrentProfileText := MyGui.Add("Text", "xm y+5 w340 Center cFF9900", "Perfil Actual: RTP 40%")
  175.  
  176. ; Estadisticas
  177. MyGui.Add("GroupBox", "xm y+10 w340 h190 Center", "Estadisticas")  ; ← Reducir altura
  178. MyGui.Add("Text", "xp+10 yp+25 w120", "Monedas totales:")
  179. TotalSpinsText := MyGui.Add("Text", "vTotalSpinsText x+5 w50", "0")
  180. MyGui.Add("Text", "xp-125 y+5 w120", "Pesos ganados:")
  181. TotalPesosText := MyGui.Add("Text", "vTotalPesosText x+5 w50", "0")
  182. MyGui.Add("Text", "xp-125 y+5 w120", "Jugador gana:")
  183. PlayerWinPercent := MyGui.Add("Text", "vPlayerWinPercent x+5 w70", "0%")
  184. MyGui.Add("Text", "xp-125 y+5 w120", "Maquina gana:")
  185. MachineWinPercent := MyGui.Add("Text", "vMachineWinPercent x+5 w70", "100%")
  186. MyGui.Add("Text", "xp-125 y+5 w120", "Giros sin ganar:")
  187. PityCounterText := MyGui.Add("Text", "vPityCounterText x+5 w70", "0/20")
  188.  
  189. ; === NUEVO INDICADOR DE RESCATE ===
  190. MyGui.Add("Text", "xp-125 y+5 w120", "Rescates:")
  191. RescueCounterText := MyGui.Add("Text", "vRescueCounterText x+5 w70", "0")
  192.  
  193. StatsBtn := MyGui.Add("Button", "xm+20 y+15 w140 h35", "Ver Detalles")
  194. StatsBtn.OnEvent("Click", ShowStats)
  195.  
  196. ResetBtn := MyGui.Add("Button", "x+20 w140 h35", "Resetear Stats")
  197. ResetBtn.OnEvent("Click", ResetStats)
  198.  
  199. ; Atajo de teclado para abrir el menú de estadísticas detalladas
  200. 9::ShowDetailedStats
  201.  
  202. MyGui.OnEvent("Close", GuiClose)
  203. MyGui.Show("AutoSize Center")
  204.  
  205. ; Inicializar después de crear la GUI
  206. UpdateSymbolList()
  207. LoadStats()
  208. LoadDetailedStats()
  209. InitializeAdvancedStats()
  210. RandomizeBonusOrder()
  211. RandomizeBonusThresholds()
  212. LoadConfig()
  213. LoadAfinacionesGuardadas() ; <--- ¡NUEVA LLAMADA PARA CARGAR AFINACIONES!
  214.  
  215. ; Inicializar RTP de sesión
  216. UpdateJokerStatus()
  217.  
  218. ;
  219. ; ===== FUNCIÓN PARA CARGAR AFINACIONES GUARDADAS (AfinacionesGuardadas.ini) =====
  220. ;
  221. LoadAfinacionesGuardadas() {
  222.     ; Declarar variables globales necesarias
  223.     global savedSettingsFile, currentProfile, symbols, pityThreshold, jokerEffectivenessRTP35
  224.     global jokerEffectivenessRTP40, jokerEffectivenessRTP45, jokerEffectivenessRTP50, multiplierProbability
  225.     global multiplierValue, safetyNetThreshold, targetRTP, RTPProfiles, CurrentProfileText
  226.     global PityCounterText, MyGui, testingMode, symbolList
  227.  
  228.     ; 1. Comprobar si el archivo de afinaciones existe
  229.     if (!FileExist(savedSettingsFile)) {
  230.         if (testingMode) {
  231.             OutputDebug("⚠️ Archivo de afinaciones '" . savedSettingsFile . "' no encontrado. Usando configuración por defecto/slot_config.ini.`n")
  232.         }
  233.         return
  234.     }
  235.  
  236.     ; 2. Leer el perfil actualmente activo del archivo de afinaciones.
  237.     ;    Asumimos que el perfil activo está guardado en la sección "Afinacion" bajo la clave "CurrentProfile".
  238.     savedProfile := IniRead(savedSettingsFile, "Afinacion", "CurrentProfile", currentProfile)
  239.  
  240.     ; 3. Cargar configuraciones guardadas si el perfil existe como sección en el INI.
  241.     ;    Los valores se leerán bajo una sección que coincide con el nombre del perfil (e.g., [RTP40])
  242.     sectionName := savedProfile
  243.  
  244.     ; Leer valores, usando "" como default para saber si fueron encontrados
  245.     loadedPity := IniRead(savedSettingsFile, sectionName, "PityThreshold", "")
  246.     loadedProb := IniRead(savedSettingsFile, sectionName, "MultiplierProbability", "")
  247.     loadedValue := IniRead(savedSettingsFile, sectionName, "MultiplierValue", "")
  248.  
  249.     ; 4. Aplicar los nuevos valores SOLO si se encontraron
  250.     if (loadedPity != "") {
  251.         pityThreshold := Integer(loadedPity)
  252.     }
  253.     if (loadedProb != "") {
  254.         multiplierProbability := Integer(loadedProb)
  255.     }
  256.     ; Asegurarse de que el valor del multiplicador se lea como Float
  257.     if (loadedValue != "") {
  258.         multiplierValue := Float(loadedValue)
  259.     }
  260.  
  261.     ; 5. Si el perfil guardado es diferente al actual, sobrescribir y recargar la configuración base.
  262.     ;    Esto asegura que los defaults de símbolos y otros parámetros se actualicen correctamente
  263.     if (savedProfile != currentProfile) {
  264.         currentProfile := savedProfile
  265.         LoadConfig() ; Se re-ejecuta LoadConfig() para aplicar los defaults del nuevo perfil (símbolos, etc.).
  266.     }
  267.  
  268.     ; 6. Mensaje de depuración y actualización de interfaz
  269.     if (testingMode) {
  270.         OutputDebug("✅ Afinaciones Guardadas cargadas. Perfil: " . currentProfile . ". Pity: " . pityThreshold . ". Prob Multi: " . multiplierProbability . ". Valor Multi: " . multiplierValue . "`n")
  271.     }
  272.  
  273.     ; Re-actualizar display después de posibles cambios en pityThreshold y targetRTP
  274.     targetRTP := RTPProfiles.Has(currentProfile) ? RTPProfiles[currentProfile] : 0.0
  275.     CurrentProfileText.Value := "Perfil Actual: " . currentProfile . " (" . targetRTP . "%)"
  276.     PityCounterText.Value := pityCounter . "/" . pityThreshold
  277.     UpdateSymbolList() ; Asegurar que la lista de símbolos se actualice.
  278. }
  279.  
  280.  
  281. ; ===== FUNCIONES =====
  282.  
  283. ; ===== FUNCIÓN EXPORTAR RESULTADOS TXT CORREGIDA =====
  284. ExportTestResults(*) {
  285.     global currentProfile, targetRTP, totalSpins, totalPesos, detailedTotalSpins, detailedTotalPesos
  286.     global multiplierProbability, multiplierValue, testingMode
  287.  
  288.     if (totalSpins = 0) {
  289.         MsgBox("No hay datos para exportar. Realiza algunos giros primero.", "Sin Datos", "48")
  290.         return
  291.     }
  292.  
  293.     ; ✅ CALCULAR RTP CON SEGURIDAD
  294.     currentRTP := 0
  295.     if (totalSpins > 0) {
  296.         currentRTP := (totalPesos / totalSpins) * 100
  297.     }
  298.  
  299.     deviation := currentRTP - targetRTP
  300.  
  301.     ; Crear nombre de archivo con timestamp
  302.     timestamp := FormatTime(, "yyyy-MM-dd_HH-mm-ss")
  303.     filename := "Resultado_Precision_" . currentProfile . "_" . timestamp . ".txt"
  304.  
  305.     content := "=== RESULTADO PRECISIÓN " . currentProfile . " ===`n"
  306.     content .= "Timestamp: " . timestamp . "`n"
  307.     content .= "Perfil: " . currentProfile . " (Objetivo: " . targetRTP . "%)`n"
  308.     content .= "Giros totales: " . totalSpins . "`n"
  309.     content .= "RTP Final: " . Round(currentRTP, 3) . "%`n"
  310.     content .= "Desviación: " . Round(deviation, 3) . "%`n"
  311.     content .= "Multiplicador final: " . multiplierProbability . "% / " . multiplierValue . "x`n"
  312.     content .= "Precisión: " . (Abs(deviation) <= 0.5 ? "✅ EXCELENTE" : (Abs(deviation) <= 1.0 ? "⚠️ ACEPTABLE" : "❌ INACEPTABLE")) . "`n"
  313.     content .= "`n"
  314.     content .= "=== DETALLES ADICIONALES ===`n"
  315.     content .= "Giros detallados: " . detailedTotalSpins . "`n"
  316.     content .= "Pesos detallados: " . detailedTotalPesos . "`n"
  317.     content .= "Fecha de análisis: " . FormatTime(, "dd/MM/yyyy HH:mm:ss") . "`n"
  318.  
  319.     try {
  320.         FileAppend(content, filename)
  321.         if (testingMode) {
  322.             OutputDebug("📊 RESULTADO EXPORTADO: " . filename . "`n")
  323.         }
  324.         MsgBox("✅ Archivo TXT exportado correctamente:`n" . filename, "Exportación Exitosa", "64")
  325.     } catch Error as e {
  326.         OutputDebug("❌ ERROR exportando: " . e.Message . "`n")
  327.         MsgBox("❌ Error al exportar archivo TXT:`n" . e.Message, "Error de Exportación", "16")
  328.     }
  329. }
  330. ; ===== SISTEMA DE RESCATE DE VOLATILIDAD MEJORADO =====
  331. CheckVolatilityRescue() {
  332.     global rescueSpinsWithoutWin, lastRescueSpin, rescueMinSpinsBetween, totalSpins
  333.     global currentProfile, rescueActivations, rescuePesos, rescueSymbols
  334.     global img1, img2, img3, symbolWin, symbolValues, totalPesos, detailedTotalPesos
  335.     global totalWins, detailedTotalWins, winStats, detailedWinStats, pesosStats, detailedPesosStats
  336.     global pityCounter, currentLosingStreak, MyGui, imgPath, rescueActive
  337.  
  338.     ; Evitar activación múltiple
  339.     if (rescueActive) {
  340.         return false
  341.     }
  342.  
  343.     ; Umbral de activación: 80 giros sin victoria y mínimo 200 giros entre rescates
  344.     if (rescueSpinsWithoutWin >= 45 && (totalSpins - lastRescueSpin) >= 120) {
  345.         rescueActive := true
  346.  
  347.         ; 🎯 MODIFICACIÓN CRÍTICA IMPLEMENTADA: SOLO SÍMBOLOS DE BAJO VALOR
  348.         lowValueRescueSymbols := ["cherry", "bell"]  ; Solo símbolos de 2-4 pesos
  349.  
  350.         ; Seleccionar símbolo de bajo valor para el rescate
  351.         rescueSymbol := lowValueRescueSymbols[Random(1, lowValueRescueSymbols.Length)]
  352.  
  353.         ; El premio será el valor natural del símbolo (2-4 pesos)
  354.         rescuePrize := symbolValues[rescueSymbol]
  355.  
  356.         if (rescuePrize > 0) {
  357.             ; Forzar combinación ganadora
  358.             img1 := rescueSymbol
  359.             img2 := rescueSymbol
  360.             img3 := rescueSymbol
  361.             symbolWin := rescueSymbol
  362.  
  363.             ; Actualizar display visual inmediatamente
  364.             MyGui["Slot1"].Value := imgPath . rescueSymbol . ".png"
  365.             MyGui["Slot2"].Value := imgPath . rescueSymbol . ".png"
  366.             MyGui["Slot3"].Value := imgPath . rescueSymbol . ".png"
  367.  
  368.             ; Procesar la victoria
  369.             HandleRescueWin(rescueSymbol, rescuePrize)
  370.  
  371.             ; Actualizar contadores
  372.             rescueActivations++
  373.             rescuePesos += rescuePrize
  374.             lastRescueSpin := totalSpins
  375.             rescueSpinsWithoutWin := 0
  376.             pityCounter := 0
  377.             currentLosingStreak := 0
  378.  
  379.             ; Actualizar display de estadísticas
  380.             UpdateStatsDisplay()
  381.  
  382.             ; Mostrar notificación en modo testing
  383.             if (testingMode) {
  384.                 OutputDebug("🎯 RESCATE ACTIVADO: " . rescueSymbol . " - " . rescuePrize . " pesos`n")
  385.             }
  386.  
  387.             rescueActive := false
  388.             return true
  389.         }
  390.         rescueActive := false
  391.     }
  392.     return false
  393. }
  394.  
  395. GetRescuePrize() {
  396.     global rescueSymbols, symbolValues
  397.  
  398.     ; Seleccionar símbolo natural para el rescate
  399.     rescueSymbol := rescueSymbols[Random(1, rescueSymbols.Length)]
  400.  
  401.     ; Pagar el valor EXACTO de la figura
  402.     return symbolValues[rescueSymbol]
  403. }
  404.  
  405. HandleRescueWin(symbol, prize) {
  406.     global totalWins, detailedTotalWins, winStats, detailedWinStats
  407.     global totalPesos, detailedTotalPesos, pesosStats, detailedPesosStats
  408.     global lastWin, pityCounter, currentLosingStreak, rescueSpinsWithoutWin
  409.  
  410.     lastWin := true
  411.     totalWins++
  412.     detailedTotalWins++
  413.     pityCounter := 0
  414.     currentLosingStreak := 0
  415.     rescueSpinsWithoutWin := 0
  416.  
  417.     ; Registrar estadísticas
  418.     winStats[symbol] := winStats.Has(symbol) ? winStats[symbol] + 1 : 1
  419.     detailedWinStats[symbol] := detailedWinStats.Has(symbol) ? detailedWinStats[symbol] + 1 : 1
  420.  
  421.     pesosStats[symbol] := pesosStats.Has(symbol) ? pesosStats[symbol] + prize : prize
  422.     detailedPesosStats[symbol] := detailedPesosStats.Has(symbol) ? detailedPesosStats[symbol] + prize : prize
  423.  
  424.     totalPesos += prize
  425.     detailedTotalPesos += prize
  426.  
  427.     ; Actualizar display
  428.     UpdateStatsDisplay()
  429.     UpdateJokerStatus()
  430. }
  431.  
  432. ; ===== NUEVA FUNCIÓN: INICIALIZAR ESTADÍSTICAS AVANZADAS =====
  433. InitializeAdvancedStats() {
  434.     global symbolFrequency, combinationStats, volatilityStats, rtpBreakdown, symbols
  435.     global rescueSpinsWithoutWin, lastRescueSpin, rescueActivations, rescuePesos, rescueActive
  436.  
  437.     ; Inicializar frecuencia de símbolos
  438.     symbolFrequency := Map()
  439.     for symbol in symbols {
  440.         symbolFrequency[symbol] := 0
  441.     }
  442.  
  443.     ; Inicializar estadísticas de combinaciones
  444.     combinationStats := Map()
  445.  
  446.     ; Inicializar métricas de volatilidad
  447.     volatilityStats := Map()
  448.     volatilityStats["CurrentLosingStreak"] := 0
  449.     volatilityStats["LongestLosingStreak"] := 0
  450.     volatilityStats["CurrentWinningStreak"] := 0
  451.     volatilityStats["LongestWinningStreak"] := 0
  452.     volatilityStats["SpinsBetweenWins"] := []
  453.     volatilityStats["WinDistribution"] := Map()
  454.  
  455.     ; Inicializar desglose de RTP
  456.     rtpBreakdown := Map()
  457.     rtpBreakdown["Base_Symbols"] := 0
  458.     rtpBreakdown["Pity_System"] := 0
  459.     rtpBreakdown["Joker_Respins"] := 0
  460.     rtpBreakdown["Multipliers"] := 0
  461.     rtpBreakdown["Jackpot"] := 0
  462.     rtpBreakdown["Rescue_System"] := 0
  463.     rtpBreakdown["Total"] := 0
  464.  
  465.     ; Inicializar sistema de rescate
  466.     rescueSpinsWithoutWin := 0
  467.     lastRescueSpin := 0
  468.     rescueActivations := 0
  469.     rescuePesos := 0
  470.     rescueActive := false
  471. }
  472.  
  473. UpdateSymbolList() {
  474.     global symbols, symbolList
  475.     symbolList := []
  476.     for symbol, count in symbols {
  477.         Loop count {
  478.             symbolList.Push(symbol)
  479.         }
  480.     }
  481. }
  482.  
  483. LoadStats() {
  484.     global statsFile, totalSpins, totalWins, totalPesos, winStats, pesosStats, symbols, jackpotCounter, pityCounter
  485.     global rescueSpinsWithoutWin, lastRescueSpin, rescueActivations, rescuePesos
  486.  
  487.     if FileExist(statsFile) {
  488.         totalSpins := Integer(IniRead(statsFile, "Stats", "TotalSpins", "0"))
  489.         totalWins := Integer(IniRead(statsFile, "Stats", "TotalWins", "0"))
  490.         totalPesos := Integer(IniRead(statsFile, "Stats", "TotalPesos", "0"))
  491.         jackpotCounter := Integer(IniRead(statsFile, "Stats", "JackpotCounter", "0"))
  492.         pityCounter := Integer(IniRead(statsFile, "Stats", "PityCounter", "0"))
  493.         rescueSpinsWithoutWin := Integer(IniRead(statsFile, "Rescue", "SpinsWithoutWin", "0"))
  494.         lastRescueSpin := Integer(IniRead(statsFile, "Rescue", "LastRescueSpin", "0"))
  495.         rescueActivations := Integer(IniRead(statsFile, "Rescue", "Activations", "0"))
  496.         rescuePesos := Integer(IniRead(statsFile, "Rescue", "TotalPesos", "0"))
  497.  
  498.         for symbol in symbols {
  499.             winStats[symbol] := Integer(IniRead(statsFile, "Symbols", symbol, "0"))
  500.             pesosStats[symbol] := Integer(IniRead(statsFile, "Pesos", symbol, "0"))
  501.         }
  502.         winStats["three_jokers"] := Integer(IniRead(statsFile, "Symbols", "three_jokers", "0"))
  503.         pesosStats["three_jokers"] := Integer(IniRead(statsFile, "Pesos", "three_jokers", "0"))
  504.     } else {
  505.         ; Inicializar valores si no existen
  506.         totalSpins := 0
  507.         totalWins := 0
  508.         totalPesos := 0
  509.         jackpotCounter := 0
  510.         pityCounter := 0
  511.         rescueSpinsWithoutWin := 0
  512.         lastRescueSpin := 0
  513.         rescueActivations := 0
  514.         rescuePesos := 0
  515.  
  516.         for symbol in symbols {
  517.             winStats[symbol] := 0
  518.             pesosStats[symbol] := 0
  519.         }
  520.         winStats["three_jokers"] := 0
  521.         pesosStats["three_jokers"] := 0
  522.     }
  523.  
  524.     ; Actualizar la interfaz
  525.     UpdateStatsDisplay()
  526. }
  527.  
  528. SaveStats() {
  529.     global statsFile, totalSpins, totalWins, totalPesos, winStats, pesosStats, jackpotCounter, pityCounter
  530.     global rescueSpinsWithoutWin, lastRescueSpin, rescueActivations, rescuePesos
  531.  
  532.     IniWrite(totalSpins, statsFile, "Stats", "TotalSpins")
  533.     IniWrite(totalWins, statsFile, "Stats", "TotalWins")
  534.     IniWrite(totalPesos, statsFile, "Stats", "TotalPesos")
  535.     IniWrite(jackpotCounter, statsFile, "Stats", "JackpotCounter")
  536.     IniWrite(pityCounter, statsFile, "Stats", "PityCounter")
  537.     IniWrite(rescueSpinsWithoutWin, statsFile, "Rescue", "SpinsWithoutWin")
  538.     IniWrite(lastRescueSpin, statsFile, "Rescue", "LastRescueSpin")
  539.     IniWrite(rescueActivations, statsFile, "Rescue", "Activations")
  540.     IniWrite(rescuePesos, statsFile, "Rescue", "TotalPesos")
  541.  
  542.     for symbol, count in winStats {
  543.         IniWrite(count, statsFile, "Symbols", symbol)
  544.         IniWrite(pesosStats[symbol], statsFile, "Pesos", symbol)
  545.     }
  546. }
  547.  
  548. LoadDetailedStats() {
  549.     global detailedStatsFile, detailedTotalSpins, detailedTotalWins, detailedTotalPesos, detailedJackpotsWon, detailedWinStats, detailedPesosStats, symbols, detailedCompensations, detailedCompensationCount, multiplierActivations, totalMultiplierPesos, detailedJokerRespines, detailedJokerRespinWins, detailedJokerRespinPesos
  550.     global currentLosingStreak, longestLosingStreak
  551.     global rescueSpinsWithoutWin, lastRescueSpin, rescueActivations, rescuePesos
  552.  
  553.     if FileExist(detailedStatsFile) {
  554.         detailedTotalSpins := Integer(IniRead(detailedStatsFile, "Stats", "TotalSpins", "0"))
  555.         detailedTotalWins := Integer(IniRead(detailedStatsFile, "Stats", "TotalWins", "0"))
  556.         detailedTotalPesos := Integer(IniRead(detailedStatsFile, "Stats", "TotalPesos", "0"))
  557.         detailedJackpotsWon := Integer(IniRead(detailedStatsFile, "Stats", "JackpotsWon", "0"))
  558.         detailedCompensations := Integer(IniRead(detailedStatsFile, "Stats", "TotalCompensations", "0"))
  559.         detailedCompensationCount := Integer(IniRead(detailedStatsFile, "Stats", "CompensationCount", "0"))
  560.         multiplierActivations := Integer(IniRead(detailedStatsFile, "Multipliers", "ActivationCount", "0"))
  561.         totalMultiplierPesos := Integer(IniRead(detailedStatsFile, "Multipliers", "TotalPesos", "0"))
  562.         detailedJokerRespines := Integer(IniRead(detailedStatsFile, "Joker", "Respines", "0"))
  563.         detailedJokerRespinWins := Integer(IniRead(detailedStatsFile, "Joker", "RespinWins", "0"))
  564.         detailedJokerRespinPesos := Integer(IniRead(detailedStatsFile, "Joker", "RespinPesos", "0"))
  565.         rescueSpinsWithoutWin := Integer(IniRead(detailedStatsFile, "Rescue", "SpinsWithoutWin", "0"))
  566.         lastRescueSpin := Integer(IniRead(detailedStatsFile, "Rescue", "LastRescueSpin", "0"))
  567.         rescueActivations := Integer(IniRead(detailedStatsFile, "Rescue", "Activations", "0"))
  568.         rescuePesos := Integer(IniRead(detailedStatsFile, "Rescue", "TotalPesos", "0"))
  569.  
  570.         for symbol in symbols {
  571.             detailedWinStats[symbol] := Integer(IniRead(detailedStatsFile, "Symbols", symbol, "0"))
  572.             detailedPesosStats[symbol] := Integer(IniRead(detailedStatsFile, "Pesos", symbol, "0"))
  573.         }
  574.         detailedWinStats["three_jokers"] := Integer(IniRead(detailedStatsFile, "Symbols", "three_jokers", "0"))
  575.         detailedPesosStats["three_jokers"] := Integer(IniRead(detailedStatsFile, "Pesos", "three_jokers", "0"))
  576.  
  577.         ; Cargar nuevas estadísticas avanzadas
  578.         currentLosingStreak := Integer(IniRead(detailedStatsFile, "AdvancedMetrics", "CurrentLosingStreak", "0"))
  579.         longestLosingStreak := Integer(IniRead(detailedStatsFile, "AdvancedMetrics", "LongestLosingStreak", "0"))
  580.  
  581.     } else {
  582.         ; Inicializar valores si no existen
  583.         detailedTotalSpins := 0
  584.         detailedTotalWins := 0
  585.         detailedTotalPesos := 0
  586.         detailedJackpotsWon := 0
  587.         detailedCompensations := 0
  588.         detailedCompensationCount := 0
  589.         multiplierActivations := 0
  590.         totalMultiplierPesos := 0
  591.         currentLosingStreak := 0
  592.         longestLosingStreak := 0
  593.         rescueSpinsWithoutWin := 0
  594.         lastRescueSpin := 0
  595.         rescueActivations := 0
  596.         rescuePesos := 0
  597.  
  598.         for symbol in symbols {
  599.             detailedWinStats[symbol] := 0
  600.             detailedPesosStats[symbol] := 0
  601.         }
  602.         detailedWinStats["three_jokers"] := 0
  603.         detailedPesosStats["three_jokers"] := 0
  604.     }
  605. }
  606.  
  607. SaveDetailedStats() {
  608.     global detailedStatsFile, detailedTotalSpins, detailedTotalWins, detailedTotalPesos, detailedJackpotsWon, detailedWinStats, detailedPesosStats, detailedCompensations, detailedCompensationCount, multiplierActivations, totalMultiplierPesos, detailedJokerRespines, detailedJokerRespinWins, detailedJokerRespinPesos
  609.     global longestLosingStreak, symbolRTPContributions, jokerRTPContribution, symbolValues
  610.     global rescueSpinsWithoutWin, lastRescueSpin, rescueActivations, rescuePesos
  611.  
  612.     RTP := 0
  613.     if (detailedTotalSpins > 0) {
  614.         RTP := Round((detailedTotalPesos / detailedTotalSpins) * 100, 2)
  615.     }
  616.  
  617.     ; Calcular RTP por símbolo
  618.     symbolRTPContributions := Map()
  619.     for symbol, pesos in detailedPesosStats {
  620.         if (detailedTotalPesos > 0) {
  621.             symbolRTPContributions[symbol] := Round((pesos / detailedTotalPesos) * RTP, 2)
  622.         } else {
  623.             symbolRTPContributions[symbol] := 0
  624.         }
  625.     }
  626.  
  627.     ; Calcular contribución del Joker
  628.     jokerRTPContribution := 0
  629.     if (detailedTotalPesos > 0) {
  630.         jokerRTPContribution := Round((detailedJokerRespinPesos / detailedTotalPesos) * RTP, 2)
  631.     }
  632.  
  633.     IniWrite(detailedTotalSpins, detailedStatsFile, "Stats", "TotalSpins")
  634.     IniWrite(detailedTotalWins, detailedStatsFile, "Stats", "TotalWins")
  635.     IniWrite(detailedTotalPesos, detailedStatsFile, "Stats", "TotalPesos")
  636.     IniWrite(detailedJackpotsWon, detailedStatsFile, "Stats", "JackpotsWon")
  637.     IniWrite(detailedCompensations, detailedStatsFile, "Stats", "TotalCompensations")
  638.     IniWrite(detailedCompensationCount, detailedStatsFile, "Stats", "CompensationCount")
  639.     IniWrite(RTP, detailedStatsFile, "Stats", "RTP")
  640.     IniWrite(multiplierActivations, detailedStatsFile, "Multipliers", "ActivationCount")
  641.     IniWrite(totalMultiplierPesos, detailedStatsFile, "Multipliers", "TotalPesos")
  642.     IniWrite(detailedJokerRespines, detailedStatsFile, "Joker", "Respines")
  643.     IniWrite(detailedJokerRespinWins, detailedStatsFile, "Joker", "RespinWins")
  644.     IniWrite(detailedJokerRespinPesos, detailedStatsFile, "Joker", "RespinPesos")
  645.     IniWrite(rescueSpinsWithoutWin, detailedStatsFile, "Rescue", "SpinsWithoutWin")
  646.     IniWrite(lastRescueSpin, detailedStatsFile, "Rescue", "LastRescueSpin")
  647.     IniWrite(rescueActivations, detailedStatsFile, "Rescue", "Activations")
  648.     IniWrite(rescuePesos, detailedStatsFile, "Rescue", "TotalPesos")
  649.  
  650.     for symbol, count in detailedWinStats {
  651.         IniWrite(count, detailedStatsFile, "Symbols", symbol)
  652.         IniWrite(detailedPesosStats[symbol], detailedStatsFile, "Pesos", symbol)
  653.     }
  654.  
  655.     ; Escribir nuevas métricas avanzadas
  656.     IniWrite(longestLosingStreak, detailedStatsFile, "AdvancedMetrics", "LongestLosingStreak")
  657.     IniWrite(currentLosingStreak, detailedStatsFile, "AdvancedMetrics", "CurrentLosingStreak")
  658.  
  659.     ; Escribir contribución RTP por símbolo
  660.     for symbol, rtp in symbolRTPContributions {
  661.         IniWrite(rtp, detailedStatsFile, "RTPBySymbol", symbol)
  662.     }
  663.  
  664.     ; Escribir contribución RTP del Joker
  665.     IniWrite(jokerRTPContribution, detailedStatsFile, "RTPBySymbol", "joker_respin_rtp")
  666. }
  667.  
  668. LoadConfig() {
  669.     ; DECLARAR EXPLÍCITAMENTE TODAS LAS VARIABLES GLOBALES
  670.     global currentProfile, symbols, pityThreshold, jokerEffectivenessRTP35, jokerEffectivenessRTP40
  671.     global jokerEffectivenessRTP45, jokerEffectivenessRTP50, multiplierProbability, multiplierValue
  672.     global safetyNetThreshold, targetRTP, RTPProfiles, CurrentProfileText, MyGui, PityCounterText
  673.     global rescueSpinsWithoutWin, rescueMinSpinsBetween
  674.  
  675.     currentProfile := IniRead(configFile, "Config", "CurrentProfile", "RTP40")
  676.  
  677.     ; CONFIGURACIÓN MEJORADA
  678.     if (currentProfile = "RTP35") {
  679.     symbols := Map("cherry", 25, "bell", 26, "lemon", 17, "star", 12, "diamond", 8, "seven", 6, "joker", 1)
  680.     pityThreshold := 25
  681.     jokerEffectivenessRTP35 := 20
  682.     multiplierProbability := 10
  683.     multiplierValue := 2.0
  684.     safetyNetThreshold := 45
  685.     rescueSpinsWithoutWin := 60
  686.     rescueMinSpinsBetween := 200
  687.     LoadAfinacionesGuardadas()
  688. }else if (currentProfile = "RTP40") {
  689.     symbols := Map("cherry", 30, "bell", 25, "lemon", 19, "star", 12, "diamond", 7, "seven", 5, "joker", 2)
  690.     pityThreshold := 25
  691.     jokerEffectivenessRTP40 := 20
  692.     multiplierProbability := 10
  693.     multiplierValue := 2.2
  694.     safetyNetThreshold := 50
  695.     rescueSpinsWithoutWin := 80
  696.     rescueMinSpinsBetween := 250
  697. } else if (currentProfile = "RTP45") {
  698.     symbols := Map("cherry", 25, "bell", 22, "lemon", 20, "star", 15, "diamond", 9, "seven", 7, "joker", 2)
  699.     pityThreshold := 25
  700.     jokerEffectivenessRTP45 := 20
  701.     multiplierProbability := 6
  702.     multiplierValue := 2.5
  703.     safetyNetThreshold := 50
  704.     rescueSpinsWithoutWin := 80
  705.     rescueMinSpinsBetween := 250
  706. } else if (currentProfile = "RTP50") {
  707.         symbols := Map("cherry", 26, "bell", 22, "lemon", 22, "star", 14, "diamond", 7, "seven", 6, "joker", 3)
  708.         pityThreshold := 25
  709.         jokerEffectivenessRTP50 := 65
  710.         multiplierProbability := 40
  711.         multiplierValue := 3.2
  712.         safetyNetThreshold := 25
  713.     }
  714.  
  715.     ; Leer valores de configuración si existen
  716.     pityThreshold := Integer(IniRead(configFile, "Config", "PityThreshold", pityThreshold))
  717.     multiplierProbability := Integer(IniRead(configFile, "Multipliers", "ActivationProbability", multiplierProbability))
  718.     multiplierValue := Float(IniRead(configFile, "Multipliers", "MultiplierValue", multiplierValue))
  719.  
  720.     targetRTP := RTPProfiles[currentProfile]
  721.     CurrentProfileText.Value := "Perfil Actual: " . currentProfile . " (" . targetRTP . "%)"
  722.  
  723.     if (currentProfile = "RTP35")
  724.         CurrentProfileText.Opt("c007ACC")
  725.     else if (currentProfile = "RTP40")
  726.         CurrentProfileText.Opt("cFF9900")
  727.     else if (currentProfile = "RTP45")
  728.         CurrentProfileText.Opt("cFF3366")
  729.     else if (currentProfile = "RTP50")
  730.         CurrentProfileText.Opt("c00FF00")
  731.  
  732.     MyGui.Title := "Tragamonedas AHK - JACKPOT a 5000 giros"
  733.     PityCounterText.Value := pityCounter . "/" . pityThreshold
  734.  
  735.     UpdateSymbolList()  ; ← ACTUALIZAR LISTA DE SÍMBOLOS
  736. }
  737.  
  738. SaveConfig() {
  739.     global configFile, currentProfile, pityThreshold, multiplierProbability, multiplierValue
  740.  
  741.     IniWrite(currentProfile, configFile, "Config", "CurrentProfile")
  742.     IniWrite(pityThreshold, configFile, "Config", "PityThreshold")
  743.     IniWrite(multiplierProbability, configFile, "Multipliers", "ActivationProbability")
  744.     IniWrite(multiplierValue, configFile, "Multipliers", "MultiplierValue")
  745. }
  746.  
  747. RandomizeBonusOrder() {
  748.     global bonusLetters, letterOrder
  749.     letterOrder := ["B", "O", "N", "U", "S"]
  750. }
  751.  
  752. RandomizeBonusThresholds() {
  753.     global bonusThresholds
  754.     bonusThresholds := []
  755.  
  756.     bonusThresholds.Push(Random(3, 5))
  757.     bonusThresholds.Push(Random(7, 9))
  758.     bonusThresholds.Push(Random(11, 13))
  759.     bonusThresholds.Push(Random(15, 17))
  760.     bonusThresholds.Push(20)
  761. }
  762.  
  763. UpdateBonusLights() {
  764.     global pityCounter, bonusThresholds, letterOrder, imgPath, MyGui
  765.  
  766.     lettersToShow := 0
  767.     Loop 5 {
  768.         if (pityCounter >= bonusThresholds[A_Index]) {
  769.             lettersToShow := A_Index
  770.         }
  771.     }
  772.  
  773.     ; Apagar todas las luces primero
  774.     Loop 5 {
  775.         MyGui["BonusSlot" . A_Index].Value := imgPath . "bonus_empty.png"
  776.     }
  777.  
  778.     ; Encender las luces en ORDEN CONSECUTIVO
  779.     Loop lettersToShow {
  780.         letter := letterOrder[A_Index]
  781.         if (letter = "B")
  782.             MyGui["BonusSlot" . A_Index].Value := imgPath . "bonus_B_red.png"
  783.         else if (letter = "O")
  784.             MyGui["BonusSlot" . A_Index].Value := imgPath . "bonus_O_blue.png"
  785.         else if (letter = "N")
  786.             MyGui["BonusSlot" . A_Index].Value := imgPath . "bonus_N_green.png"
  787.         else if (letter = "U")
  788.             MyGui["BonusSlot" . A_Index].Value := imgPath . "bonus_U_yellow.png"
  789.         else if (letter = "S")
  790.             MyGui["BonusSlot" . A_Index].Value := imgPath . "bonus_S_purple.png"
  791.     }
  792. }
  793.  
  794. ShowProfileMenu(*) {
  795.     try {
  796.         ProfileMenu := Menu()
  797.         ProfileMenu.Add("RTP 35%", SetRTPProfile.Bind("RTP35"))
  798.         ProfileMenu.Add("RTP 40%", SetRTPProfile.Bind("RTP40"))
  799.         ProfileMenu.Add("RTP 45%", SetRTPProfile.Bind("RTP45"))
  800.         ProfileMenu.Add("RTP 50%", SetRTPProfile.Bind("RTP50"))
  801.         ProfileMenu.Show()
  802.     }
  803. }
  804.  
  805. SetRTPProfile(profileName, *) {
  806.     ; DECLARAR EXPLÍCITAMENTE TODAS LAS VARIABLES GLOBALES
  807.     global currentProfile, targetRTP, RTPProfiles, symbols, MyGui
  808.     global pityThreshold, jokerEffectivenessRTP35, jokerEffectivenessRTP40
  809.     global jokerEffectivenessRTP45, jokerEffectivenessRTP50, multiplierProbability, multiplierValue
  810.     global safetyNetThreshold
  811.     global rescueSpinsWithoutWin, rescueMinSpinsBetween
  812.  
  813.     currentProfile := profileName
  814.     targetRTP := RTPProfiles[profileName]
  815.  
  816.    ; === CONFIGURACIÓN OPTIMIZADA GAMIFICACIÓN ===
  817.    if (currentProfile = "RTP35") {
  818.     symbols := Map("cherry", 25, "bell", 26, "lemon", 17, "star", 12, "diamond", 8, "seven", 6, "joker", 1)
  819.     pityThreshold := 25
  820.     jokerEffectivenessRTP35 := 20
  821.     multiplierProbability := 10
  822.     multiplierValue := 2.0
  823.     safetyNetThreshold := 45
  824.     rescueSpinsWithoutWin := 60
  825.     rescueMinSpinsBetween := 200
  826.     LoadAfinacionesGuardadas()
  827. } else if (currentProfile = "RTP40") {
  828.     symbols := Map("cherry", 30, "bell", 25, "lemon", 19, "star", 12, "diamond", 7, "seven", 5, "joker", 2)
  829.     pityThreshold := 25
  830.     jokerEffectivenessRTP40 := 20
  831.     multiplierProbability := 10
  832.     multiplierValue := 2.2
  833.     safetyNetThreshold := 50
  834.     rescueSpinsWithoutWin := 80
  835.     rescueMinSpinsBetween := 250
  836. } else if (currentProfile = "RTP45") {
  837.     symbols := Map("cherry", 25, "bell", 22, "lemon", 20, "star", 15, "diamond", 9, "seven", 7, "joker", 2)
  838.     pityThreshold := 25
  839.     jokerEffectivenessRTP45 := 20
  840.     multiplierProbability := 6
  841.     multiplierValue := 2.5
  842.     safetyNetThreshold := 50
  843.     rescueSpinsWithoutWin := 80
  844.     rescueMinSpinsBetween := 250
  845. } else if (currentProfile = "RTP50") {
  846.         symbols := Map("cherry", 26, "bell", 22, "lemon", 22, "star", 14, "diamond", 7, "seven", 6, "joker", 3)
  847.         pityThreshold := 25
  848.         jokerEffectivenessRTP50 := 65
  849.         multiplierProbability := 40
  850.         multiplierValue := 3.2
  851.         safetyNetThreshold := 25
  852.     }
  853.  
  854.     UpdateSymbolList()
  855.     UpdateProfileDisplay()
  856.     ResetStats(0)
  857.  
  858.     ; GUARDAR CONFIGURACIÓN
  859.     SaveConfig()
  860.  
  861.     MyGui.Opt("+OwnDialogs")
  862.     MsgBox("RTP configurado al " . targetRTP . "%", "Perfil Cambiado", "64")
  863.     MyGui.Title := "Tragamonedas AHK - JACKPOT a 5000 giros"
  864.     UpdateJokerStatus()
  865. }
  866.  
  867. ResetProfileSettings() {
  868.     global currentProfile, pityThreshold
  869.  
  870.     if (currentProfile = "RTP35") {
  871.         pityThreshold := 25
  872.     } else if (currentProfile = "RTP40") {
  873.         pityThreshold := 25
  874.     } else if (currentProfile = "RTP45") {
  875.         pityThreshold := 25
  876.     } else if (currentProfile = "RTP50") {
  877.         pityThreshold := 25
  878.     }
  879.  
  880.     SaveConfig()
  881. }
  882.  
  883. UpdateProfileDisplay() {
  884.     global currentProfile, targetRTP, CurrentProfileText, MyGui
  885.  
  886.     CurrentProfileText.Value := "Perfil Actual: " . currentProfile . " (" . targetRTP . "%)"
  887.     if (currentProfile = "RTP35")
  888.         CurrentProfileText.Opt("c007ACC")
  889.     else if (currentProfile = "RTP40")
  890.         CurrentProfileText.Opt("cFF9900")
  891.     else if (currentProfile = "RTP45")
  892.         CurrentProfileText.Opt("cFF3366")
  893.     else if (currentProfile = "RTP50")
  894.         CurrentProfileText.Opt("c00FF00")
  895.  
  896.     MyGui.Title := "Tragamonedas AHK - JACKPOT a 5000 giros"
  897. }
  898.  
  899. CalculatePlayerWinPercent() {
  900.     global totalSpins, totalPesos
  901.     if (totalSpins = 0)
  902.         return "0%"
  903.     playerWinPercent := (totalPesos / totalSpins) * 100
  904.     return Round(playerWinPercent, 1) . "%"
  905. }
  906.  
  907. CalculateMachineWinPercent() {
  908.     global totalSpins, totalPesos
  909.     if (totalSpins = 0)
  910.         return "100%"
  911.     machineWinPercent := ((totalSpins - totalPesos) / totalSpins) * 100
  912.     return Round(machineWinPercent, 1) . "%"
  913. }
  914.  
  915. UpdateWinPercentages() {
  916.     PlayerWinPercent.Value := CalculatePlayerWinPercent()
  917.     MachineWinPercent.Value := CalculateMachineWinPercent()
  918. }
  919.  
  920. UpdateJokerStatus() {
  921.     global currentRTP, totalSpins, totalPesos, MyGui, imgPath
  922.  
  923.     if (totalSpins > 0) {
  924.         currentRTP := (totalPesos / totalSpins) * 100
  925.     } else {
  926.         currentRTP := 0
  927.     }
  928.  
  929.     if (currentRTP < targetRTP) {
  930.         MyGui["JokerLogo"].Value := imgPath . "joker_giro_gratis_prendido.png"
  931.     } else {
  932.         MyGui["JokerLogo"].Value := imgPath . "joker_giro_gratis_apagado.png"
  933.     }
  934. }
  935.  
  936. Start1000Spins(*) {
  937.     global spinning, autoSpin, testingMode, autoSpinCount, maxAutoSpins
  938.     if spinning
  939.         return
  940.     autoSpin := true
  941.     testingMode := true
  942.     autoSpinCount := 0
  943.     maxAutoSpins := 1000
  944.     SpinBtn.Enabled := false
  945.     Spin1000Btn.Enabled := false
  946.     Spin10000Btn.Enabled := false
  947.     SetTimer(AutoSpinLoop, 80)
  948. }
  949.  
  950. Start10000Spins(*) {
  951.     global spinning, autoSpin, testingMode, autoSpinCount, maxAutoSpins
  952.     if spinning
  953.         return
  954.     autoSpin := true
  955.     testingMode := true
  956.     autoSpinCount := 0
  957.     maxAutoSpins := 50000
  958.     SpinBtn.Enabled := false
  959.     Spin1000Btn.Enabled := false
  960.     Spin10000Btn.Enabled := false
  961.     SetTimer(AutoSpinLoop, 80)
  962. }
  963.  
  964. StartUltraTest(*) {
  965.     global spinning, autoSpin, testingMode, autoSpinCount, maxAutoSpins
  966.     global symbols, symbolList  ; ← Asegurar que estas globales estén declaradas
  967.     if spinning
  968.         return
  969.      ; === AGREGAR ESTA LÍNEA CRÍTICA ===
  970.     UpdateSymbolList()  ; ← ¡ACTUALIZAR LA LISTA DE SÍMBOLOS!
  971.  
  972.     MyGui.Opt("+OwnDialogs")
  973.     MsgBox("🚀 INICIANDO PRUEBA ULTRA-RÁPIDA`n`n• 50,000 giros en 5-9 minutos`n• RTP teórico puro (sin ajustes dinámicos)`n• Sin animaciones ni sonidos`n• Resultados válidos al 100%", "Modo Pruebas Rápido", "64")
  974.  
  975.     autoSpin := true
  976.     testingMode := true
  977.     autoSpinCount := 0
  978.     maxAutoSpins := 50000
  979.     SpinBtn.Enabled := false
  980.     Spin1000Btn.Enabled := false
  981.     Spin10000Btn.Enabled := false
  982.     UltraTestBtn.Enabled := false
  983.     UltraTestBtn.Text := "🔥 PROCESANDO... " . autoSpinCount . "/50000"
  984.  
  985.     SetTimer(UltraSpinLoop, 1)
  986. }
  987.  
  988. UltraSpinLoop() {
  989.     ; DECLARAR TODAS LAS VARIABLES GLOBALES UNA POR UNA
  990.     global autoSpin, autoSpinCount, maxAutoSpins, spinning, totalSpins, detailedTotalSpins
  991.     global img1, img2, img3, symbolWin, symbolList, targetRTP, totalPesos
  992.     global detailedJokerRespines, detailedJokerRespinWins, UltraTestBtn
  993.     global symbolFrequency, combinationStats, jokerActivations
  994.     global jackpotCounter, jackpotValue, detailedJackpotsWon, pityCounter
  995.     global detailedTotalPesos, testingMode, UpdateBonusLights, symbolValues
  996.     global detailedJokerRespinPesos, jokerWins, jokerPesos
  997.     global jackpotActivations, jackpotWins, jackpotPesos
  998.     global currentLosingStreak, longestLosingStreak
  999.     global currentProfile, safetyNetActive, safetyNetThreshold
  1000.     global rescueSpinsWithoutWin, lastRescueSpin, rescueMinSpinsBetween, rescueActivations, rescuePesos
  1001.  
  1002.     if spinning || autoSpinCount >= maxAutoSpins {
  1003.         if (autoSpinCount >= maxAutoSpins) {
  1004.             SetTimer(UltraSpinLoop, 0)
  1005.             autoSpin := false
  1006.             testingMode := false
  1007.             lastUpdateCount := 0
  1008.  
  1009.             SaveStats()
  1010.             SaveDetailedStats()
  1011.  
  1012.             ; ✅ NUEVO: EXPORTAR RESULTADOS AUTOMÁTICAMENTE - AGREGADO
  1013.             if (testingMode) {
  1014.                 ExportTestResults()
  1015.             }
  1016.  
  1017.             SpinBtn.Enabled := true
  1018.             Spin1000Btn.Enabled := true
  1019.             Spin10000Btn.Enabled := true
  1020.             UltraTestBtn.Enabled := true
  1021.             UltraTestBtn.Text := "🔥 PRUEBA ULTRA-RÁPIDA 50000"
  1022.  
  1023.             currentRTP := (totalPesos / totalSpins) * 100
  1024.             MyGui.Opt("+OwnDialogs")
  1025.             MsgBox("✅ PRUEBA COMPLETADA`n`nGiros: " . maxAutoSpins . "`nRTP Final: " . Round(currentRTP, 2) . "%`nDesviación: " . Round(currentRTP - targetRTP, 2) . "%`n`n📊 Datos guardados en archivos INI", "Resultados Ultra-Rápidos", "64")
  1026.         }
  1027.         return
  1028.     }
  1029.  
  1030.     Critical true
  1031.  
  1032.     totalSpins++
  1033.     detailedTotalSpins++
  1034.     autoSpinCount++
  1035.  
  1036.         ; ACTUALIZACIÓN GARANTIZADA CADA 100 GIROS
  1037.     if (Mod(autoSpinCount, 100) == 0) {
  1038.         UltraTestBtn.Text := "🔥 PROCESANDO... " . autoSpinCount . "/50000"
  1039.         UpdateStatsDisplay()
  1040.     }
  1041.  
  1042.     randIndex := Random(1, symbolList.Length)
  1043.     img1 := symbolList[randIndex]
  1044.     randIndex := Random(1, symbolList.Length)
  1045.     img2 := symbolList[randIndex]
  1046.     randIndex := Random(1, symbolList.Length)
  1047.     img3 := symbolList[randIndex]
  1048.  
  1049.     symbolFrequency[img1] := symbolFrequency.Get(img1, 0) + 1
  1050.     symbolFrequency[img2] := symbolFrequency.Get(img2, 0) + 1
  1051.     symbolFrequency[img3] := symbolFrequency.Get(img3, 0) + 1
  1052.  
  1053.     symbolWin := CheckWinCombination(img1, img2, img3)
  1054.  
  1055.     ; ===== VERIFICAR RESCATE DE VOLATILIDAD MEJORADO =====
  1056.     if (!symbolWin || symbolWin = false) {
  1057.         rescueSpinsWithoutWin++
  1058.         if (CheckVolatilityRescue()) {
  1059.             ; El rescate ya manejó la victoria, continuar al siguiente giro
  1060.             Critical false
  1061.             return
  1062.         }
  1063.     } else {
  1064.         rescueSpinsWithoutWin := 0
  1065.     }
  1066.  
  1067.     if (symbolWin is String && symbolWin != "") {
  1068.         HandleTripleWinUltra()
  1069.     } else if (img1 = "joker" || img2 = "joker" || img3 = "joker") {
  1070.         currentRTP := 0
  1071.         if (totalSpins > 0) {
  1072.             currentRTP := (totalPesos / totalSpins) * 100
  1073.         }
  1074.  
  1075.         if (currentRTP < targetRTP) {
  1076.             jokerActivations += 1
  1077.             detailedJokerRespines++
  1078.  
  1079.             if (img1 != "joker") {
  1080.                 randIndex := Random(1, symbolList.Length)
  1081.                 img1 := symbolList[randIndex]
  1082.             }
  1083.             if (img2 != "joker") {
  1084.                 randIndex := Random(1, symbolList.Length)
  1085.                 img2 := symbolList[randIndex]
  1086.             }
  1087.             if (img3 != "joker") {
  1088.                 randIndex := Random(1, symbolList.Length)
  1089.                 img3 := symbolList[randIndex]
  1090.             }
  1091.  
  1092.             symbolWin := CheckWinCombination(img1, img2, img3)
  1093.             if (symbolWin is String && symbolWin != "") {
  1094.                 ; ✅ BLOQUE CORREGIDO - CONTADORES DE JOKER FIJOS
  1095.                 pesosGanados := symbolValues.Has(symbolWin) ? symbolValues[symbolWin] : 2
  1096.                 HandleTripleWinUltra()
  1097.                 detailedJokerRespinWins += 1  ; ← SOLO 1 VICTORIA POR RESPIN
  1098.                 detailedJokerRespinPesos += pesosGanados
  1099.                 jokerWins += 1
  1100.                 jokerPesos += pesosGanados
  1101.             } else {
  1102.                 HandleLossUltra()
  1103.             }
  1104.         } else {
  1105.             HandleLossUltra()
  1106.         }
  1107.     } else {
  1108.         HandleLossUltra()
  1109.     }
  1110.  
  1111.     ; VERIFICACIÓN JACKPOT
  1112.     if (jackpotCounter >= 5000) {
  1113.         totalPesos += jackpotValue
  1114.         detailedTotalPesos += jackpotValue
  1115.         detailedJackpotsWon++
  1116.         jackpotCounter := 0
  1117.         pityCounter := 0
  1118.         UpdateBonusLights()
  1119.  
  1120.         ; ✅ CONTADORES DE SISTEMAS DE AYUDA
  1121.         jackpotActivations += 1
  1122.         jackpotWins += 1
  1123.         jackpotPesos += jackpotValue
  1124.     }
  1125.  
  1126.     Critical false
  1127. }
  1128.  
  1129. HandleTripleWinUltra() {
  1130.     global lastWin, totalWins, detailedTotalWins, winStats, detailedWinStats, pityCounter
  1131.     global totalPesos, detailedTotalPesos, pesosStats, detailedPesosStats, symbolWin, symbolValues
  1132.     global totalMultiplierPesos, multiplierActivations, multiplierValue, multiplierProbability
  1133.     global detailedJokerRespinPesos, combinationStats, multiplierActivationsCount, multiplierWins, multiplierPesosExtra
  1134.     global jokerWins, jokerPesos
  1135.     global currentLosingStreak, safetyNetActive, safetyNetBoost
  1136.     global rescueSpinsWithoutWin
  1137.  
  1138.     lastWin := true
  1139.     currentLosingStreak := 0
  1140.     rescueSpinsWithoutWin := 0
  1141.     totalWins++
  1142.     detailedTotalWins++
  1143.     pityCounter := 0
  1144.  
  1145.     pesosGanados := symbolValues.Has(symbolWin) ? symbolValues[symbolWin] : 2
  1146.  
  1147.     combinationStats[symbolWin] := combinationStats.Get(symbolWin, 0) + 1
  1148.  
  1149.     if (symbolWin = "three_jokers") {
  1150.         winStats["three_jokers"] := winStats.Has("three_jokers") ? winStats["three_jokers"] + 1 : 1
  1151.         detailedWinStats["three_jokers"] := detailedWinStats.Has("three_jokers") ? detailedWinStats["three_jokers"] + 1 : 1
  1152.     } else {
  1153.         winStats[symbolWin] := winStats.Has(symbolWin) ? winStats[symbolWin] + 1 : 1
  1154.         detailedWinStats[symbolWin] := detailedWinStats.Has(symbolWin) ? detailedWinStats[symbolWin] + 1 : 1
  1155.  
  1156.         if (Random(1, 100) <= (100 / multiplierProbability)) {
  1157.             multiplierActivations++
  1158.             multiplierActivationsCount++
  1159.             baseValue := symbolValues.Has(symbolWin) ? symbolValues[symbolWin] : 0
  1160.             multipliedValue := Round(baseValue * multiplierValue)
  1161.             totalMultiplierPesos += (multipliedValue - baseValue)
  1162.             multiplierPesosExtra += (multipliedValue - baseValue)
  1163.             multiplierWins += 1
  1164.             pesosGanados := multipliedValue
  1165.         }
  1166.     }
  1167.  
  1168.     totalPesos += pesosGanados
  1169.     detailedTotalPesos += pesosGanados
  1170.  
  1171.     if (symbolWin = "three_jokers") {
  1172.         pesosStats["three_jokers"] := pesosStats.Has("three_jokers") ? pesosStats["three_jokers"] + pesosGanados : pesosGanados
  1173.         detailedPesosStats["three_jokers"] := detailedPesosStats.Has("three_jokers") ? detailedPesosStats["three_jokers"] + pesosGanados : pesosGanados
  1174.     } else {
  1175.         pesosStats[symbolWin] := pesosStats.Has(symbolWin) ? pesosStats[symbolWin] + pesosGanados : pesosGanados
  1176.         detailedPesosStats[symbolWin] := detailedPesosStats.Has(symbolWin) ? detailedPesosStats[symbolWin] + pesosGanados : pesosGanados
  1177.     }
  1178. }
  1179.  
  1180. HandleLossUltra() {
  1181.     global lastWin, pityCounter, jackpotCounter, totalPesos, detailedTotalPesos, detailedCompensations, detailedCompensationCount, testingMode, currentProfile, totalSpins, pityThreshold, pityActivations, pityWins, pityPesos
  1182.     global currentLosingStreak, longestLosingStreak
  1183.     global safetyNetActive, safetyNetThreshold
  1184.     global rescueSpinsWithoutWin
  1185.  
  1186.     lastWin := false
  1187.     currentLosingStreak++
  1188.     if (currentLosingStreak > longestLosingStreak) {
  1189.         longestLosingStreak := currentLosingStreak
  1190.     }
  1191.     pityCounter++
  1192.     jackpotCounter++
  1193.     rescueSpinsWithoutWin++  ; ← CONTAR GIROS SIN GANAR
  1194.  
  1195.     ; ✅ VERIFICAR SAFETY NET PARA RTP35
  1196.     if (currentProfile = "RTP35" && currentLosingStreak >= safetyNetThreshold && !safetyNetActive) {
  1197.         safetyNetActive := false
  1198.         ; En modo ultra, simplemente forzar una victoria
  1199.         symbolWin := "bell"
  1200.         HandleTripleWinUltra()
  1201.         safetyNetActive := false
  1202.         return
  1203.     }
  1204.  
  1205.     if (pityCounter >= pityThreshold) {
  1206.         pityCounter := 0
  1207.         compensation := 4  ; ← COMPENSACIÓN FIJA DE 4 PESOS
  1208.  
  1209.         if (compensation > 0) {
  1210.             detailedCompensations += compensation
  1211.             detailedCompensationCount++
  1212.             totalPesos += compensation
  1213.             detailedTotalPesos += compensation
  1214.  
  1215.             pityActivations += 1
  1216.             pityWins += 1
  1217.             pityPesos += compensation
  1218.         }
  1219.     }
  1220. }
  1221.  
  1222. ExportTestData(*) {
  1223.     global currentProfile, targetRTP, detailedTotalSpins, detailedTotalWins, detailedTotalPesos
  1224.     global symbolFrequency, combinationStats, symbols, symbolValues
  1225.     global detailedCompensations, detailedJokerRespines, detailedJokerRespinWins, detailedJokerRespinPesos
  1226.     global multiplierActivations, totalMultiplierPesos, detailedJackpotsWon, jackpotValue
  1227.     global longestLosingStreak, currentLosingStreak
  1228.     global pityActivations, pityWins, pityPesos, multiplierActivationsCount, multiplierWins, multiplierPesosExtra
  1229.     global jokerActivations, jokerWins, jokerPesos, jackpotActivations, jackpotWins, jackpotPesos
  1230.     global detailedPesosStats, detailedWinStats
  1231.     global rescueSpinsWithoutWin, rescueActivations, rescuePesos
  1232.  
  1233.     try {
  1234.         timestamp := FormatTime(, "yyyy-MM-dd_HH-mm-ss")
  1235.         filename := "RTP_Analysis_" . currentProfile . "_" . timestamp . ".csv"
  1236.  
  1237.         ; ✅ CALCULAR RTP CON SEGURIDAD
  1238.         currentRTP := 0
  1239.         if (detailedTotalSpins > 0) {
  1240.             currentRTP := (detailedTotalPesos / detailedTotalSpins) * 100
  1241.         }
  1242.  
  1243.         csvContent := ""
  1244.  
  1245.         csvContent .= "ANÁLISIS RTP - PERFIL " . currentProfile . "`n"
  1246.         csvContent .= "Timestamp,RTP_Teórico,RTP_Calculado,Total_Giros,Total_Victorias,Total_Pesos`n"
  1247.         csvContent .= timestamp . "," . targetRTP . "," . Round(currentRTP, 2) . "," . detailedTotalSpins . "," . detailedTotalWins . "," . detailedTotalPesos . "`n`n"
  1248.  
  1249.         ; ✅ SECCIÓN FRECUENCIA CORREGIDA - EVITA DIVISIÓN POR CERO
  1250.         csvContent .= "FRECUENCIA DE SÍMBOLOS`n"
  1251.         csvContent .= "Símbolo,Veces_Salió,Probabilidad_Real,Probabilidad_Teórica`n"
  1252.  
  1253.         totalSymbolAppearances := 0
  1254.         for symbol, count in symbolFrequency {
  1255.             totalSymbolAppearances += count
  1256.         }
  1257.  
  1258.         if (totalSymbolAppearances = 0) {
  1259.             ; Si no hay datos, mostrar valores en cero
  1260.             for symbol, count in symbolFrequency {
  1261.                 theoreticalProbability := symbols.Has(symbol) ? (symbols[symbol] / 100) * 100 : 0
  1262.                 csvContent .= symbol . "," . count . ",0%," . theoreticalProbability . "%`n"
  1263.             }
  1264.         } else {
  1265.             ; Calcular probabilidades normalmente
  1266.             for symbol, count in symbolFrequency {
  1267.                 realProbability := (count / totalSymbolAppearances) * 100
  1268.                 theoreticalProbability := symbols.Has(symbol) ? (symbols[symbol] / 100) * 100 : 0
  1269.                 csvContent .= symbol . "," . count . "," . Round(realProbability, 2) . "%," . theoreticalProbability . "%`n"
  1270.             }
  1271.         }
  1272.         csvContent .= "`n"
  1273.  
  1274.         ; ✅ SECCIÓN COMBINACIONES GANADORAS CON SEGURIDAD
  1275.         csvContent .= "COMBINACIONES GANADORAS`n"
  1276.         csvContent .= "Combinación,Veces,Probabilidad,Pesos_Generados,Valor_Promedio`n"
  1277.  
  1278.         totalWins := detailedTotalWins
  1279.         if (totalWins = 0) {
  1280.             ; Si no hay victorias, mostrar combinaciones en cero
  1281.             for combination, count in combinationStats {
  1282.                 pesosGenerados := detailedPesosStats.Has(combination) ? detailedPesosStats[combination] : 0
  1283.                 csvContent .= combination . "," . count . ",0%," . pesosGenerados . ",0`n"
  1284.             }
  1285.         } else {
  1286.             ; Calcular probabilidades normalmente
  1287.             for combination, count in combinationStats {
  1288.                 probability := (count / totalWins) * 100
  1289.                 pesosGenerados := detailedPesosStats.Has(combination) ? detailedPesosStats[combination] : 0
  1290.                 valorPromedio := (count > 0) ? (pesosGenerados / count) : 0
  1291.                 csvContent .= combination . "," . count . "," . Round(probability, 2) . "%," . pesosGenerados . "," . Round(valorPromedio, 2) . "`n"
  1292.             }
  1293.         }
  1294.         csvContent .= "`n"
  1295.  
  1296.         ; ✅ SISTEMAS DE AYUDA CON VERIFICACIONES
  1297.         csvContent .= "SISTEMAS DE AYUDA`n"
  1298.         csvContent .= "Sistema,Activaciones,Victorias,Tasa_Éxito,Pesos_Generados`n"
  1299.  
  1300.         ; Pity Counter
  1301.         pitySuccessRate := (pityActivations > 0) ? ((pityWins / pityActivations) * 100) : 0
  1302.         csvContent .= "Pity_Counter," . pityActivations . "," . pityWins . "," . Round(pitySuccessRate, 1) . "%," . pityPesos . "`n"
  1303.  
  1304.         ; Multiplicador
  1305.         multiplierSuccessRate := (multiplierActivationsCount > 0) ? ((multiplierWins / multiplierActivationsCount) * 100) : 0
  1306.         csvContent .= "Multiplicador," . multiplierActivationsCount . "," . multiplierWins . "," . Round(multiplierSuccessRate, 1) . "%," . multiplierPesosExtra . "`n"
  1307.  
  1308.         ; Joker Respin
  1309.         jokerSuccessRate := (jokerActivations > 0) ? ((jokerWins / jokerActivations) * 100) : 0
  1310.         csvContent .= "Joker_Respin," . jokerActivations . "," . jokerWins . "," . Round(jokerSuccessRate, 1) . "%," . jokerPesos . "`n"
  1311.  
  1312.         ; Jackpot
  1313.         jackpotSuccessRate := (jackpotActivations > 0) ? ((jackpotWins / jackpotActivations) * 100) : 0
  1314.         csvContent .= "Jackpot," . jackpotActivations . "," . jackpotWins . "," . Round(jokerSuccessRate, 1) . "%," . jackpotPesos . "`n"
  1315.  
  1316.         ; Sistema de Rescate
  1317.         rescueSuccessRate := (rescueActivations > 0) ? 100 : 0  ; Siempre tiene éxito cuando se activa
  1318.         csvContent .= "Rescate_Volatilidad," . rescueActivations . "," . rescueActivations . "," . rescueSuccessRate . "%," . rescuePesos . "`n"
  1319.  
  1320.         csvContent .= "`n"
  1321.  
  1322.         ; ✅ VOLATILIDAD
  1323.         csvContent .= "VOLATILIDAD`n"
  1324.         csvContent .= "Métrica,Valor`n"
  1325.         csvContent .= "Racha_Pérdida_Más_Larga," . longestLosingStreak . "`n"
  1326.         csvContent .= "Racha_Pérdida_Actual," . currentLosingStreak . "`n"
  1327.         csvContent .= "Giros_Sin_Victoria_Actual," . rescueSpinsWithoutWin . "`n"
  1328.  
  1329.         avgSpinsBetweenWins := (detailedTotalWins > 0) ? (detailedTotalSpins / detailedTotalWins) : 0
  1330.         csvContent .= "Giros_Promedio_Entre_Victorias," . Round(avgSpinsBetweenWins, 1) . "`n"
  1331.  
  1332.         winRate := (detailedTotalSpins > 0) ? ((detailedTotalWins / detailedTotalSpins) * 100) : 0
  1333.         csvContent .= "Tasa_de_Victoria," . Round(winRate, 2) . "%`n"
  1334.         csvContent .= "`n"
  1335.  
  1336.         ; ✅ DESGLOSE RTP CON VERIFICACIONES
  1337.         csvContent .= "DESGLOSE RTP`n"
  1338.         csvContent .= "Componente,Contribución_RTP`n"
  1339.  
  1340.         ; Calcular con seguridad contra división por cero
  1341.         if (detailedTotalSpins > 0) {
  1342.             baseRTP := ((detailedTotalPesos - detailedCompensations - detailedJokerRespinPesos - totalMultiplierPesos - (detailedJackpotsWon * jackpotValue) - rescuePesos) / detailedTotalSpins) * 100
  1343.             pityRTP := (detailedCompensations / detailedTotalSpins) * 100
  1344.             jokerRTP := (detailedJokerRespinPesos / detailedTotalSpins) * 100
  1345.             multiplierRTP := (totalMultiplierPesos / detailedTotalSpins) * 100
  1346.             jackpotRTP := ((detailedJackpotsWon * jackpotValue) / detailedTotalSpins) * 100
  1347.             rescueRTP := (rescuePesos / detailedTotalSpins) * 100
  1348.         } else {
  1349.             baseRTP := 0
  1350.             pityRTP := 0
  1351.             jokerRTP := 0
  1352.             multiplierRTP := 0
  1353.             jackpotRTP := 0
  1354.             rescueRTP := 0
  1355.         }
  1356.  
  1357.         csvContent .= "Símbolos_Base," . Round(baseRTP, 2) . "%`n"
  1358.         csvContent .= "Sistema_Pity," . Round(pityRTP, 2) . "%`n"
  1359.         csvContent .= "Joker_Respins," . Round(jokerRTP, 2) . "%`n"
  1360.         csvContent .= "Multiplicadores," . Round(multiplierRTP, 2) . "%`n"
  1361.         csvContent .= "Jackpot," . Round(jackpotRTP, 2) . "%`n"
  1362.         csvContent .= "Rescate_Volatilidad," . Round(rescueRTP, 2) . "%`n"
  1363.         csvContent .= "TOTAL," . Round(currentRTP, 2) . "%`n"
  1364.  
  1365.         FileAppend(csvContent, filename)
  1366.  
  1367.         MyGui.Opt("+OwnDialogs")
  1368.         MsgBox("📈 DATOS EXPORTADOS`n`nArchivo: " . filename . "`n`nContiene análisis completo para afinación RTP", "Exportación Completada", "64")
  1369.  
  1370.     } catch Error as e {
  1371.         MyGui.Opt("+OwnDialogs")
  1372.         MsgBox("❌ ERROR al exportar datos:`n" . e.Message . "`nLínea: " . e.Line, "Error de Exportación", "16")
  1373.     }
  1374. }
  1375.  
  1376. UpdateStatsDisplay() {
  1377.     global totalSpins, totalWins, totalPesos, jackpotCounter, pityCounter, pityThreshold, multiplierActivations, rescueActivations
  1378.  
  1379.     TotalSpinsText.Value := totalSpins
  1380.     TotalPesosText.Value := totalPesos
  1381.     JackpotCounterText.Value := jackpotCounter . "/5000"
  1382.     PityCounterText.Value := pityCounter . "/" . pityThreshold
  1383.     RescueCounterText.Value := rescueActivations  ; ← NUEVO: Mostrar rescates
  1384.     UpdateWinPercentages()
  1385. }
  1386.  
  1387. StartSpin(*) {
  1388.     global spinning, totalSpins, detailedTotalSpins, SpinBtn, Spin1000Btn, Spin10000Btn, imgPath, spinInterval, spinDuration
  1389.     global currentProfile
  1390.  
  1391.     if spinning
  1392.         return
  1393.  
  1394.     spinning := true
  1395.     SpinBtn.Enabled := false
  1396.     SpinBtn.Text := "Girando..."
  1397.     Spin1000Btn.Enabled := false
  1398.     Spin10000Btn.Enabled := false
  1399.  
  1400.     totalSpins++
  1401.     detailedTotalSpins++
  1402.  
  1403.     UpdateStatsDisplay()
  1404.     SaveStats()
  1405.  
  1406.     Loop 3
  1407.         MyGui["Slot" . A_Index].Value := imgPath . "question.png"
  1408.  
  1409.     SetTimer(SpinSlots, spinInterval)
  1410.     SetTimer(StopSpin, -spinDuration)
  1411. }
  1412.  
  1413. SpinSlots() {
  1414.     global symbolList, imgPath, MyGui, img1, img2, img3
  1415.  
  1416.     randIndex := Random(1, symbolList.Length)
  1417.     img1 := symbolList[randIndex]
  1418.  
  1419.     randIndex := Random(1, symbolList.Length)
  1420.     img2 := symbolList[randIndex]
  1421.  
  1422.     randIndex := Random(1, symbolList.Length)
  1423.     img3 := symbolList[randIndex]
  1424.  
  1425.     Loop 3 {
  1426.         currentImg := (A_Index = 1) ? img1 : (A_Index = 2) ? img2 : img3
  1427.         imgFile := imgPath . currentImg . ".png"
  1428.         fallback := imgPath . "question.png"
  1429.  
  1430.         if FileExist(imgFile)
  1431.             MyGui["Slot" . A_Index].Value := imgFile
  1432.         else
  1433.             MyGui["Slot" . A_Index].Value := fallback
  1434.     }
  1435. }
  1436.  
  1437. StopSpin() {
  1438.     global spinning, MyGui, imgPath, img1, img2, img3, symbolWin, jackpotCounter, pityCounter, pityThreshold
  1439.     global SpinBtn, Spin1000Btn, Spin10000Btn, lastWin
  1440.     global currentLosingStreak, longestLosingStreak
  1441.     global currentProfile, rescueSpinsWithoutWin
  1442.  
  1443.     SetTimer(SpinSlots, 0)
  1444.     Critical true
  1445.  
  1446.     try {
  1447.         MyGui["Slot1"].Value := imgPath . img1 . ".png"
  1448.         MyGui["Slot2"].Value := imgPath . img2 . ".png"
  1449.         MyGui["Slot3"].Value := imgPath . img3 . ".png"
  1450.     }
  1451.     Sleep 30
  1452.  
  1453.     symbolWin := CheckWinCombination(img1, img2, img3)
  1454.  
  1455.     ; ===== VERIFICAR RESCATE DE VOLATILIDAD MEJORADO =====
  1456.     if (!symbolWin || symbolWin = false) {
  1457.         rescueSpinsWithoutWin++
  1458.         if (CheckVolatilityRescue()) {
  1459.             ; El rescate manejó la victoria completamente
  1460.             spinning := false
  1461.             SpinBtn.Enabled := true
  1462.             SpinBtn.Text := "1 Moneda"
  1463.             SpinBtn.Opt("+Default")
  1464.             if (!autoSpin) {
  1465.                 Spin1000Btn.Enabled := true
  1466.                 Spin10000Btn.Enabled := true
  1467.             }
  1468.  
  1469.             ; Mostrar mensaje discreto en modo normal
  1470.             if (!testingMode && !autoSpin) {
  1471.                 SetTimer(ShowRescueMessage, -500)
  1472.             }
  1473.  
  1474.             SaveStats()
  1475.             SaveDetailedStats()
  1476.             Critical false
  1477.             return
  1478.         }
  1479.     } else {
  1480.         rescueSpinsWithoutWin := 0
  1481.     }
  1482.  
  1483.     if (symbolWin is String && symbolWin != "") {
  1484.         HandleTripleWin()
  1485.     }
  1486.  
  1487.     if (jackpotCounter >= 5000) {
  1488.         HandleJackpotWin()
  1489.     }
  1490.  
  1491.     if (!symbolWin || symbolWin = false) {
  1492.         if (img1 = "joker" || img2 = "joker" || img3 = "joker") {
  1493.             HandleJokerRespin()
  1494.         } else {
  1495.             HandleLoss()
  1496.         }
  1497.     }
  1498.  
  1499.     if (!lastWin) {
  1500.         currentLosingStreak++
  1501.     } else {
  1502.         if (currentLosingStreak > longestLosingStreak) {
  1503.             longestLosingStreak := currentLosingStreak
  1504.         }
  1505.         currentLosingStreak := 0
  1506.     }
  1507.  
  1508.     PityCounterText.Value := pityCounter . "/" . pityThreshold
  1509.     JackpotCounterText.Value := jackpotCounter . "/5000"
  1510.     UpdateBonusLights()
  1511.     UpdateWinPercentages()
  1512.     UpdateJokerStatus()
  1513.  
  1514.     spinning := false
  1515.     SpinBtn.Enabled := true
  1516.     SpinBtn.Text := "1 Moneda"
  1517.     SpinBtn.Opt("+Default")
  1518.     if (!autoSpin) {
  1519.         Spin1000Btn.Enabled := true
  1520.         Spin10000Btn.Enabled := true
  1521.     }
  1522.  
  1523.     SaveStats()
  1524.     SaveDetailedStats()
  1525.     Critical false
  1526. }
  1527.  
  1528. ShowRescueMessage() {
  1529.     MyGui.Opt("+OwnDialogs")
  1530.     MsgBox("¡Combinación ganadora!`n`nHas obtenido una victoria.", "¡Ganaste!", "64")
  1531. }
  1532.  
  1533. CheckWinCombination(reel1, reel2, reel3) {
  1534.     symbolsInReels := [reel1, reel2, reel3]
  1535.     symbolCounts := Map()
  1536.     jokerCount := 0
  1537.  
  1538.     for symbol in symbolsInReels {
  1539.         if (symbol = "joker") {
  1540.             jokerCount++
  1541.         } else {
  1542.             symbolCounts[symbol] := symbolCounts.Has(symbol) ? symbolCounts[symbol] + 1 : 1
  1543.         }
  1544.     }
  1545.  
  1546.     for symbol, count in symbolCounts {
  1547.         if (count = 3) {
  1548.             return symbol
  1549.         }
  1550.     }
  1551.  
  1552.     for symbol, count in symbolCounts {
  1553.         if (count = 2 && jokerCount >= 1) {
  1554.             return symbol
  1555.         }
  1556.     }
  1557.  
  1558.     for symbol, count in symbolCounts {
  1559.         if (count = 1 && jokerCount >= 2) {
  1560.             return symbol
  1561.         }
  1562.     }
  1563.  
  1564.     if (jokerCount = 3) {
  1565.         return "three_jokers"
  1566.     }
  1567.  
  1568.     return false
  1569. }
  1570.  
  1571. ; ===== NUEVAS FUNCIONES RTP35 MEJORADO =====
  1572.  
  1573. CheckSafetyNet() {
  1574.     global currentLosingStreak, safetyNetThreshold, safetyNetActive, safetyNetBoost
  1575.     global symbols, currentProfile, totalSpins
  1576.  
  1577.     ; Solo activar para RTP35/RTP40/RTP45/RTP50 y si no está ya activo
  1578.     if ((currentProfile != "RTP35" && currentProfile != "RTP40" && currentProfile != "RTP45" && currentProfile != "RTP50") || safetyNetActive || currentLosingStreak < safetyNetThreshold) {
  1579.         return
  1580.     }
  1581.  
  1582.     safetyNetActive := true
  1583.     safetyNetBoost := true
  1584.  
  1585.     ; ✅ DISTRIBUCIÓN DE EMERGENCIA TEMPORAL POR PERFIL
  1586.     if (currentProfile = "RTP35") {
  1587.         emergencySymbols := Map(
  1588.             "cherry", 25,    ; -3 temporalmente
  1589.             "bell", 22,      ; -2
  1590.             "lemon", 14,     ; -2
  1591.             "star", 18,      ; +3 (aumentar significativamente)
  1592.             "diamond", 12,   ; +3 (más oportunidades alto valor)
  1593.             "seven", 8,      ; +1 (más símbolos premium)
  1594.             "joker", 1       ; = (mantener)
  1595.         )
  1596.     } else if (currentProfile = "RTP40") {
  1597.         emergencySymbols := Map(
  1598.             "cherry", 28,    ; -3 temporalmente
  1599.             "bell", 23,      ; -3
  1600.             "lemon", 15,     ; -2
  1601.             "star", 15,      ; +3 (aumentar significativamente)
  1602.             "diamond", 10,   ; +2 (más oportunidades alto valor)
  1603.             "seven", 6,      ; +1 (más símbolos premium)
  1604.             "joker", 1       ; = (mantener)
  1605.         )
  1606.     } else if (currentProfile = "RTP45") {
  1607.         emergencySymbols := Map(
  1608.             "cherry", 38,    ; -2 temporalmente
  1609.             "bell", 23,      ; -2
  1610.             "lemon", 14,     ; -1
  1611.             "star", 10,      ; +2 (aumentar significativamente)
  1612.             "diamond", 6,    ; +1 (más oportunidades alto valor)
  1613.             "seven", 5,      ; +1 (más símbolos premium)
  1614.             "joker", 2       ; = (mantener)
  1615.         )
  1616.     } else if (currentProfile = "RTP50") {
  1617.         emergencySymbols := Map(
  1618.             "cherry", 45,    ; -3 temporalmente
  1619.             "bell", 26,      ; -2
  1620.             "lemon", 16,     ; -2
  1621.             "star", 12,      ; +2 (aumentar significativamente)
  1622.             "diamond", 7,    ; +1 (más oportunidades alto valor)
  1623.             "seven", 6,      ; +1 (más símbolos premium)
  1624.             "joker", 2       ; = (mantener)
  1625.         )
  1626.     }
  1627.  
  1628.     ; Aplicar distribución de emergencia
  1629.     ApplyEmergencyDistribution(emergencySymbols)
  1630.  
  1631.     ; Mostrar notificación
  1632.     MyGui.Opt("+OwnDialogs")
  1633.     MsgBox("🛡️ SISTEMA DE SEGURIDAD " . currentProfile . " ACTIVADO`n`nDespués de " . currentLosingStreak . " giros sin ganar`nProbabilidades premium aumentadas temporalmente`n¡La suerte está por cambiar!", "Red de Seguridad", "64")
  1634.  
  1635.     ; Restaurar después de 10 giros o primera victoria
  1636.     SetTimer(RestoreOriginalDistribution, 10000)
  1637. }
  1638.  
  1639. ApplyEmergencyDistribution(emergencySymbols) {
  1640.     global symbols
  1641.     symbols := emergencySymbols.Clone()
  1642.     UpdateSymbolList()
  1643. }
  1644.  
  1645. RestoreOriginalDistribution() {
  1646.     global safetyNetActive, safetyNetBoost, currentProfile
  1647.  
  1648.     if (safetyNetActive && currentProfile = "RTP35") {
  1649.         LoadConfig()  ; Recargar configuración original
  1650.         safetyNetActive := false
  1651.         safetyNetBoost := false
  1652.  
  1653.         ; Notificar restauración silenciosa
  1654.         SetTimer(ClearRestoreMessage, 3000)
  1655.     }
  1656. }
  1657.  
  1658. ClearRestoreMessage() {
  1659. }
  1660.  
  1661. GetWeightedPremiumSymbol() {
  1662.     premiumSymbols := ["seven", "diamond", "star", "bell", "lemon"]
  1663.     weights := [15, 20, 25, 25, 15]  ; Probabilidades ponderadas
  1664.  
  1665.     totalWeight := 0
  1666.     for weight in weights {
  1667.         totalWeight += weight
  1668.     }
  1669.  
  1670.     randomValue := Random(1, totalWeight)
  1671.     cumulativeWeight := 0
  1672.  
  1673.     Loop premiumSymbols.Length {
  1674.         cumulativeWeight += weights[A_Index]
  1675.         if (randomValue <= cumulativeWeight) {
  1676.             return premiumSymbols[A_Index]
  1677.         }
  1678.     }
  1679.  
  1680.     return "star"  ; Fallback
  1681. }
  1682.  
  1683. EnhancedJokerRespin(spin1, spin2, spin3) {
  1684.     global img1, img2, img3, symbolList, MyGui, imgPath, jokerEffectivenessRTP35, jokerEffectivenessRTP40, jokerEffectivenessRTP45, jokerEffectivenessRTP50, currentProfile
  1685.  
  1686.     ; ✅ OBTENER EFECTIVIDAD CORRECTA SEGÚN PERFIL
  1687.     jokerEffectiveness := 35  ; Default
  1688.     if (currentProfile = "RTP35") {
  1689.         jokerEffectiveness := jokerEffectivenessRTP35
  1690.     } else if (currentProfile = "RTP40") {
  1691.         jokerEffectiveness := jokerEffectivenessRTP40
  1692.     } else if (currentProfile = "RTP45") {
  1693.         jokerEffectiveness := jokerEffectivenessRTP45
  1694.     } else if (currentProfile = "RTP50") {
  1695.         jokerEffectiveness := jokerEffectivenessRTP50
  1696.     }
  1697.  
  1698.     ; ✅ ALGORITMO MEJORADO - MÁS PROBABILIDAD DE ÉXITO
  1699.     if (spin1 && Random(1, 100) <= jokerEffectiveness) {
  1700.         ; Forzar símbolo de mayor valor
  1701.         img1 := GetWeightedPremiumSymbol()
  1702.         MyGui["Slot1"].Value := imgPath . img1 . ".png"
  1703.     } else if (spin1) {
  1704.         randIndex := Random(1, symbolList.Length)
  1705.         img1 := symbolList[randIndex]
  1706.         MyGui["Slot1"].Value := imgPath . img1 . ".png"
  1707.     }
  1708.     Sleep 120
  1709.  
  1710.     if (spin2 && Random(1, 100) <= jokerEffectiveness) {
  1711.         img2 := GetWeightedPremiumSymbol()
  1712.         MyGui["Slot2"].Value := imgPath . img2 . ".png"
  1713.     } else if (spin2) {
  1714.         randIndex := Random(1, symbolList.Length)
  1715.         img2 := symbolList[randIndex]
  1716.         MyGui["Slot2"].Value := imgPath . img2 . ".png"
  1717.     }
  1718.     Sleep 120
  1719.  
  1720.     if (spin3 && Random(1, 100) <= jokerEffectiveness) {
  1721.         img3 := GetWeightedPremiumSymbol()
  1722.         MyGui["Slot3"].Value := imgPath . img3 . ".png"
  1723.     } else if (spin3) {
  1724.         randIndex := Random(1, symbolList.Length)
  1725.         img3 := symbolList[randIndex]
  1726.         MyGui["Slot3"].Value := imgPath . img3 . ".png"
  1727.     }
  1728.     Sleep 120
  1729. }
  1730.  
  1731. StandardJokerRespin(spin1, spin2, spin3) {
  1732.     global img1, img2, img3, symbolList, MyGui, imgPath
  1733.  
  1734.     if (spin1) {
  1735.         randIndex := Random(1, symbolList.Length)
  1736.         img1 := symbolList[randIndex]
  1737.         MyGui["Slot1"].Value := imgPath . img1 . ".png"
  1738.     }
  1739.     Sleep 100
  1740.  
  1741.     if (spin2) {
  1742.         randIndex := Random(1, symbolList.Length)
  1743.         img2 := symbolList[randIndex]
  1744.         MyGui["Slot2"].Value := imgPath . img2 . ".png"
  1745.     }
  1746.     Sleep 100
  1747.  
  1748.     if (spin3) {
  1749.         randIndex := Random(1, symbolList.Length)
  1750.         img3 := symbolList[randIndex]
  1751.         MyGui["Slot3"].Value := imgPath . img3 . ".png"
  1752.     }
  1753.     Sleep 100
  1754. }
  1755.  
  1756. ShowEnhancedJokerWin(symbolWin) {
  1757.     global symbolValues
  1758.     pesosGanados := symbolValues.Has(symbolWin) ? symbolValues[symbolWin] : 0
  1759.     MyGui.Opt("+OwnDialogs")
  1760.     MsgBox("¡JOKER MEJORADO!`n`nCombinación: " . symbolWin . "`nPremio: " . pesosGanados . " pesos`n`n¡El comodín te sonríe!", "Joker Potenciado", "64")
  1761. }
  1762.  
  1763. ; ===== FUNCIONES EXISTENTES MODIFICADAS =====
  1764.  
  1765. HandleJokerRespin() {
  1766.     global img1, img2, img3, spinning, imgPath, symbolList, testingMode, totalSpins, totalPesos, targetRTP, symbolWin
  1767.     global detailedJokerRespines, detailedJokerRespinWins, detailedJokerRespinPesos
  1768.     global SpinBtn, Spin1000Btn, Spin10000Btn, MyGui, detailedTotalWins, detailedTotalPesos, detailedWinStats, detailedPesosStats
  1769.     global jokerActivations, jokerWins, jokerPesos, currentProfile
  1770.  
  1771.     currentRTP := 0
  1772.     if (totalSpins > 0) {
  1773.         currentRTP := (totalPesos / totalSpins) * 100
  1774.     }
  1775.  
  1776.     ; ✅ UMBRAL MÁS FLEXIBLE PARA PERFILES BAJOS
  1777.     activationThreshold := (currentProfile = "RTP35") ? targetRTP + 3 : targetRTP
  1778.  
  1779.     if (currentRTP < activationThreshold) {
  1780.         jokerActivations += 1
  1781.         detailedJokerRespines++
  1782.  
  1783.         spinning := true
  1784.         SpinBtn.Enabled := false
  1785.         SpinBtn.Text := "Re-Girando..."
  1786.         Spin1000Btn.Enabled := false
  1787.         Spin10000Btn.Enabled := false
  1788.  
  1789.         spin1 := (img1 != "joker")
  1790.         spin2 := (img2 != "joker")
  1791.         spin3 := (img3 != "joker")
  1792.  
  1793.         ; ✅ ANIMACIÓN MEJORADA PARA TODOS LOS PERFILES
  1794.         Loop 8 {
  1795.             if (spin1) {
  1796.                 randIndex := Random(1, symbolList.Length)
  1797.                 MyGui["Slot1"].Value := imgPath . symbolList[randIndex] . ".png"
  1798.             }
  1799.             if (spin2) {
  1800.                 randIndex := Random(1, symbolList.Length)
  1801.                 MyGui["Slot2"].Value := imgPath . symbolList[randIndex] . ".png"
  1802.             }
  1803.             if (spin3) {
  1804.                 randIndex := Random(1, symbolList.Length)
  1805.                 MyGui["Slot3"].Value := imgPath . symbolList[randIndex] . ".png"
  1806.             }
  1807.             Sleep 40
  1808.         }
  1809.  
  1810.         ; ✅ RESULTADOS MEJORADOS PARA TODOS LOS PERFILES
  1811.         EnhancedJokerRespin(spin1, spin2, spin3)
  1812.  
  1813.         symbolWin := CheckWinCombination(img1, img2, img3)
  1814.  
  1815.         if (symbolWin is String && symbolWin != "") {
  1816.             HandleTripleWin()
  1817.  
  1818.             if (!testingMode && (currentProfile = "RTP35" || currentProfile = "RTP50")) {
  1819.                 ShowEnhancedJokerWin(symbolWin)
  1820.             }
  1821.             pesosGanados := symbolValues.Has(symbolWin) ? symbolValues[symbolWin] : 2
  1822.             detailedJokerRespinWins++
  1823.             detailedJokerRespinPesos += pesosGanados
  1824.             jokerWins += 1
  1825.             jokerPesos += pesosGanados
  1826.         } else {
  1827.             HandleJokerRespinLoss()
  1828.         }
  1829.  
  1830.         spinning := false
  1831.         SpinBtn.Enabled := true
  1832.         SpinBtn.Text := "1 Moneda"
  1833.         SpinBtn.Opt("+Default")
  1834.         Spin1000Btn.Enabled := true
  1835.         Spin10000Btn.Enabled := true
  1836.     } else {
  1837.         HandleLoss()
  1838.     }
  1839. }
  1840.  
  1841. HandleJokerRespinLoss() {
  1842.     global lastWin, testingMode
  1843.     lastWin := false
  1844. }
  1845.  
  1846. HandleJackpotWin() {
  1847.     global lastWin, totalWins, detailedTotalWins, pityCounter, totalPesos, detailedTotalPesos, detailedJackpotsWon, jackpotValue, jackpotCounter, testingMode, imgPath, symbolList, MyGui
  1848.     global jackpotActivations, jackpotWins, jackpotPesos
  1849.  
  1850.     if (!testingMode) {
  1851.     }
  1852.     Loop 5 {
  1853.         randIndex := Random(1, symbolList.Length)
  1854.         MyGui["Slot1"].Value := imgPath . symbolList[randIndex] . ".png"
  1855.         randIndex := Random(1, symbolList.Length)
  1856.         MyGui["Slot2"].Value := imgPath . symbolList[randIndex] . ".png"
  1857.         randIndex := Random(1, symbolList.Length)
  1858.         MyGui["Slot3"].Value := imgPath . symbolList[randIndex] . ".png"
  1859.         Sleep 30
  1860.     }
  1861.     Loop 3 {
  1862.         randIndex := Random(1, symbolList.Length)
  1863.         MyGui["Slot1"].Value := imgPath . symbolList[randIndex] . ".png"
  1864.         randIndex := Random(1, symbolList.Length)
  1865.         MyGui["Slot2"].Value := imgPath . symbolList[randIndex] . ".png"
  1866.         randIndex := Random(1, symbolList.Length)
  1867.         MyGui["Slot3"].Value := imgPath . symbolList[randIndex] . ".png"
  1868.         Sleep 80
  1869.     }
  1870.     MyGui["Slot1"].Value := imgPath . "jackpot.png"
  1871.     if (!testingMode) {
  1872.     }
  1873.     Sleep 150
  1874.     MyGui["Slot2"].Value := imgPath . "jackpot.png"
  1875.     if (!testingMode) {
  1876.     }
  1877.     Sleep 150
  1878.     MyGui["Slot3"].Value := imgPath . "jackpot.png"
  1879.     if (!testingMode) {
  1880.     }
  1881.     Sleep 250
  1882.     Loop 2 {
  1883.         MyGui["Slot1"].Value := imgPath . "jackpot_glow.png"
  1884.         MyGui["Slot2"].Value := imgPath . "jackpot_glow.png"
  1885.         MyGui["Slot3"].Value := imgPath . "jackpot_glow.png"
  1886.         Sleep 100
  1887.         MyGui["Slot1"].Value := imgPath . "jackpot.png"
  1888.         MyGui["Slot2"].Value := imgPath . "jackpot.png"
  1889.         MyGui["Slot3"].Value := imgPath . "jackpot.png"
  1890.         Sleep 100
  1891.     }
  1892.     lastWin := true
  1893.     totalWins++
  1894.     detailedTotalWins++
  1895.     pityCounter := 0
  1896.     totalPesos += jackpotValue
  1897.     detailedTotalPesos += jackpotValue
  1898.     detailedJackpotsWon++
  1899.     jackpotActivations += 1
  1900.     jackpotWins += 1
  1901.     jackpotPesos += jackpotValue
  1902.     if (!testingMode) {
  1903.     }
  1904.     jackpotCounter := 0
  1905.     Loop 5 {
  1906.         MyGui["BonusSlot" . A_Index].Value := imgPath . "bonus_empty.png"
  1907.     }
  1908.     RandomizeBonusOrder()
  1909.     RandomizeBonusThresholds()
  1910.     UpdateStatsDisplay()
  1911.     UpdateJokerStatus()
  1912. }
  1913.  
  1914. HandleTripleWin() {
  1915.     global lastWin, totalWins, detailedTotalWins, winStats, detailedWinStats, pityCounter, totalPesos, detailedTotalPesos, pesosStats, detailedPesosStats, totalMultiplierPesos, multiplierActivations, symbolWin, testingMode, multiplierValue, imgPath, symbolValues, MyGui
  1916.     global safetyNetActive, safetyNetBoost, currentProfile
  1917.     global rescueSpinsWithoutWin
  1918.  
  1919.     if (!symbolWin is String || symbolWin = "") {
  1920.         return
  1921.     }
  1922.  
  1923.     lastWin := true
  1924.     totalWins++
  1925.     detailedTotalWins++
  1926.     pityCounter := 0
  1927.     rescueSpinsWithoutWin := 0  ; ← RESETEAR CONTADOR DE RESCATE
  1928.  
  1929.     ; ✅ BONUS EXTRA POR VICTORIA DURANTE SAFETY NET
  1930.     ;bonusPesos := 0
  1931.     ;if (safetyNetBoost && currentProfile = "RTP35") {
  1932.       ;  bonusPesos := 10  ; Bonus adicional por romper racha mala
  1933.       ;  totalPesos += bonusPesos
  1934.       ;  detailedTotalPesos += bonusPesos
  1935.     ;}
  1936.  
  1937.     ; ✅ DESACTIVAR SAFETY NET SI ESTÁ ACTIVO
  1938.     if (safetyNetActive) {
  1939.         RestoreOriginalDistribution()
  1940.  
  1941.        ; if (!testingMode && bonusPesos > 0) {
  1942.          ;   MsgBox("¡VICTORIA CON BONUS DE SEGURIDAD!`n`nBonus extra: " . bonusPesos . " pesos`nRacha mala rota - Sistema normal restaurado", "¡Éxito!", "64")
  1943.        ; }
  1944.     }
  1945.  
  1946.     if (!symbolValues.Has(symbolWin)) {
  1947.         pesosGanados := 2
  1948.     } else {
  1949.         pesosGanados := symbolValues[symbolWin]
  1950.     }
  1951.  
  1952.     if (symbolWin = "three_jokers") {
  1953.         if (!testingMode) {
  1954.         }
  1955.         winStats["three_jokers"] := winStats.Has("three_jokers") ? winStats["three_jokers"] + 1 : 1
  1956.         detailedWinStats["three_jokers"] := detailedWinStats.Has("three_jokers") ? detailedWinStats["three_jokers"] + 1 : 1
  1957.     } else {
  1958.         if (winStats.Has(symbolWin)) {
  1959.             winStats[symbolWin]++
  1960.             detailedWinStats[symbolWin]++
  1961.         } else {
  1962.             winStats[symbolWin] := 1
  1963.             detailedWinStats[symbolWin] := 1
  1964.         }
  1965.  
  1966.         extraMultiplier := CheckMultiplierActivation()
  1967.         if (extraMultiplier > 0) {
  1968.             totalMultiplied := pesosGanados + extraMultiplier
  1969.  
  1970.             if (!testingMode) {
  1971.             }
  1972.  
  1973.             pesosGanados := totalMultiplied
  1974.             totalMultiplierPesos += extraMultiplier
  1975.         } else {
  1976.             if (!testingMode && symbolWin != "three_jokers") {
  1977.             }
  1978.         }
  1979.     }
  1980.  
  1981.     totalPesos += pesosGanados
  1982.     detailedTotalPesos += pesosGanados
  1983.  
  1984.     if (symbolWin = "three_jokers") {
  1985.         pesosStats["three_jokers"] := pesosStats.Has("three_jokers") ? pesosStats["three_jokers"] + pesosGanados : pesosGanados
  1986.         detailedPesosStats["three_jokers"] := detailedPesosStats.Has("three_jokers") ? detailedPesosStats["three_jokers"] + pesosGanados : pesosGanados
  1987.     } else if (pesosStats.Has(symbolWin)) {
  1988.         pesosStats[symbolWin] += pesosGanados
  1989.         detailedPesosStats[symbolWin] += pesosGanados
  1990.     } else {
  1991.         pesosStats[symbolWin] := pesosGanados
  1992.         detailedPesosStats[symbolWin] := pesosGanados
  1993.     }
  1994.  
  1995.     UpdateStatsDisplay()
  1996.     UpdateJokerStatus()
  1997. }
  1998.  
  1999. CheckMultiplierActivation() {
  2000.     global multiplierProbability, multiplierValue, testingMode, multiplierActivations, symbolWin, symbolValues
  2001.  
  2002.     if (symbolWin = "three_jokers") {
  2003.         return 0
  2004.     }
  2005.  
  2006.     if (Random(1, 100) <= (100 / multiplierProbability)) {
  2007.         multiplierActivations++
  2008.  
  2009.         baseValue := symbolValues.Has(symbolWin) ? symbolValues[symbolWin] : 0
  2010.         multipliedValue := Round(baseValue * multiplierValue)
  2011.  
  2012.         return multipliedValue - baseValue
  2013.     }
  2014.     return 0
  2015. }
  2016.  
  2017. HandleLoss() {
  2018.     global lastWin, pityCounter, jackpotCounter, totalPesos, detailedTotalPesos, detailedCompensations, detailedCompensationCount, testingMode, currentProfile, totalSpins, pityThreshold, MyGui, imgPath
  2019.     global currentLosingStreak, longestLosingStreak
  2020.     global safetyNetActive, safetyNetThreshold
  2021.     global rescueSpinsWithoutWin
  2022.  
  2023.     lastWin := false
  2024.     pityCounter++
  2025.     jackpotCounter++
  2026.     rescueSpinsWithoutWin++  ; ← CONTAR GIROS SIN GANAR
  2027.  
  2028.     UpdateBonusLights()
  2029.  
  2030.     ; ✅ VERIFICAR SAFETY NET PARA TODOS LOS PERFILES MEJORADOS
  2031.     if ((currentProfile = "RTP35" || currentProfile = "RTP40" || currentProfile = "RTP45" || currentProfile = "RTP50") && currentLosingStreak >= safetyNetThreshold && !safetyNetActive) {
  2032.         CheckSafetyNet()
  2033.     }
  2034.  
  2035.     if (pityCounter >= pityThreshold) {
  2036.         pityCounter := 0
  2037.         compensation := 4  ; ← COMPENSACIÓN FIJA DE 4 PESOS
  2038.         compensationMessage := "¡Compensación!`n" . pityThreshold . " giros sin ganar"
  2039.         showCompensationMessage := true
  2040.  
  2041.         if (compensation > 0) {
  2042.             detailedCompensations += compensation
  2043.             detailedCompensationCount++
  2044.             totalPesos += compensation
  2045.             detailedTotalPesos += compensation
  2046.  
  2047.             if (!testingMode && showCompensationMessage) {
  2048.                 ; Mensaje opcional de compensación
  2049.             }
  2050.         }
  2051.     }
  2052. }
  2053.  
  2054. AutoSpinLoop() {
  2055.     global autoSpin, autoSpinCount, maxAutoSpins, spinning, SpinBtn, Spin1000Btn, Spin10000Btn
  2056.     if spinning
  2057.         return
  2058.     if (autoSpinCount >= maxAutoSpins) {
  2059.         SetTimer(AutoSpinLoop, 0)
  2060.         autoSpin := false
  2061.         testingMode := false
  2062.         SpinBtn.Enabled := true
  2063.         Spin1000Btn.Enabled := true
  2064.         Spin10000Btn.Enabled := true
  2065.         return
  2066.     }
  2067.     StartSpin()
  2068.     autoSpinCount++
  2069. }
  2070.  
  2071. ResetStats(reason, *) {
  2072.     global totalSpins, totalWins, totalPesos, winStats, pesosStats, jackpotCounter, pityCounter
  2073.     global detailedTotalSpins, detailedTotalWins, detailedTotalPesos, detailedJackpotsWon, detailedCompensations, detailedCompensationCount, detailedWinStats, detailedPesosStats
  2074.     global detailedJokerRespines, detailedJokerRespinWins, detailedJokerRespinPesos
  2075.     global multiplierActivations, totalMultiplierPesos
  2076.     global currentLosingStreak, longestLosingStreak
  2077.     global symbolFrequency, combinationStats, volatilityStats, rtpBreakdown
  2078.     global pityActivations, pityWins, pityPesos, multiplierActivationsCount, multiplierWins, multiplierPesosExtra
  2079.     global jokerActivations, jokerWins, jokerPesos, jackpotActivations, jackpotWins, jackpotPesos
  2080.     global safetyNetActive, safetyNetBoost
  2081.     global rescueSpinsWithoutWin, lastRescueSpin, rescueActivations, rescuePesos, rescueActive
  2082.  
  2083.     totalSpins := 0
  2084.     totalWins := 0
  2085.     totalPesos := 0
  2086.     jackpotCounter := 0
  2087.     pityCounter := 0
  2088.  
  2089.     detailedTotalSpins := 0
  2090.     detailedTotalWins := 0
  2091.     detailedTotalPesos := 0
  2092.     detailedJackpotsWon := 0
  2093.     detailedCompensations := 0
  2094.     detailedCompensationCount := 0
  2095.     detailedJokerRespines := 0
  2096.     detailedJokerRespinWins := 0
  2097.     detailedJokerRespinPesos := 0
  2098.     multiplierActivations := 0
  2099.     totalMultiplierPesos := 0
  2100.  
  2101.     currentLosingStreak := 0
  2102.     longestLosingStreak := 0
  2103.  
  2104.     pityActivations := 0
  2105.     pityWins := 0
  2106.     pityPesos := 0
  2107.     multiplierActivationsCount := 0
  2108.     multiplierWins := 0
  2109.     multiplierPesosExtra := 0
  2110.     jokerActivations := 0
  2111.     jokerWins := 0
  2112.     jokerPesos := 0
  2113.     jackpotActivations := 0
  2114.     jackpotWins := 0
  2115.     jackpotPesos := 0
  2116.  
  2117.     safetyNetActive := false
  2118.     safetyNetBoost := false
  2119.  
  2120.     rescueSpinsWithoutWin := 0
  2121.     lastRescueSpin := 0
  2122.     rescueActivations := 0
  2123.     rescuePesos := 0
  2124.     rescueActive := false
  2125.  
  2126.     InitializeAdvancedStats()
  2127.  
  2128.     for symbol in symbols {
  2129.         winStats[symbol] := 0
  2130.         pesosStats[symbol] := 0
  2131.         detailedWinStats[symbol] := 0
  2132.         detailedPesosStats[symbol] := 0
  2133.     }
  2134.     winStats["three_jokers"] := 0
  2135.     pesosStats["three_jokers"] := 0
  2136.     detailedWinStats["three_jokers"] := 0
  2137.     detailedPesosStats["three_jokers"] := 0
  2138.  
  2139.     UpdateStatsDisplay()
  2140.     UpdateJokerStatus()
  2141.     SaveStats()
  2142.     SaveDetailedStats()
  2143.     if (!reason = 0) {
  2144.         MyGui.Opt("+OwnDialogs")
  2145.         MsgBox("Estadísticas reseteadas", "Reset", "64")
  2146.     }
  2147. }
  2148.  
  2149. ShowStats(*) {
  2150.     MyGui.Opt("+OwnDialogs")
  2151.     MsgBox("Estadísticas de la sesión actual:`nTotal giros: " . totalSpins . "`nVictorias: " . totalWins . "`nPesos ganados: " . totalPesos, "Estadísticas de sesión", "64")
  2152. }
  2153.  
  2154. ShowDetailedStats(*) {
  2155.     global DetailedStatsGui, ReportEdit, detailedTotalSpins, detailedTotalWins, detailedTotalPesos, detailedJackpotsWon, detailedCompensations, detailedCompensationCount, detailedJokerRespines, detailedJokerRespinWins, detailedJokerRespinPesos
  2156.     global longestLosingStreak, symbolRTPContributions, jokerRTPContribution
  2157.     global rescueSpinsWithoutWin, rescueActivations, rescuePesos
  2158.  
  2159.     LoadDetailedStats()
  2160.  
  2161.     if (IsObject(DetailedStatsGui)) {
  2162.         DetailedStatsGui.Destroy()
  2163.     }
  2164.     DetailedStatsGui := Gui("+Resize", "Reporte Detallado")
  2165.     DetailedStatsGui.SetFont("s10", "Courier New")
  2166.     ReportEdit := DetailedStatsGui.Add("Edit", "w600 h500 ReadOnly vReportEdit")
  2167.     ReportEdit.Value := GenerateDetailedReport()
  2168.     DetailedStatsGui.Show("Center")
  2169. }
  2170.  
  2171. GenerateDetailedReport() {
  2172.     global detailedTotalSpins, detailedTotalWins, detailedTotalPesos, detailedJackpotsWon, detailedCompensations, detailedCompensationCount, detailedWinStats, detailedPesosStats, symbols
  2173.     global detailedJokerRespines, detailedJokerRespinWins, detailedJokerRespinPesos
  2174.     global multiplierActivations, totalMultiplierPesos
  2175.     global longestLosingStreak, symbolRTPContributions, jokerRTPContribution
  2176.     global rescueSpinsWithoutWin, rescueActivations, rescuePesos
  2177.  
  2178.     report := ""
  2179.     report .= "============== REPORTE DETALLADO DE SIMULACIÓN ==============`n`n"
  2180.     report .= "--- GENERAL ---`n"
  2181.     report .= "Giros Totales: " . detailedTotalSpins . "`n"
  2182.     report .= "Victorias Totales: " . detailedTotalWins . "`n"
  2183.     report .= "Pesos Ganados Totales: " . detailedTotalPesos . "`n"
  2184.     report .= "Multiplicadores Activados: " . multiplierActivations . "`n"
  2185.     report .= "Pesos por Multiplicadores: " . totalMultiplierPesos . "`n"
  2186.     report .= "Rescates de Volatilidad: " . rescueActivations . "`n"
  2187.     report .= "Pesos por Rescates: " . rescuePesos . "`n"
  2188.     if (detailedTotalSpins > 0) {
  2189.         report .= "RTP Calculado: " . Round((detailedTotalPesos / detailedTotalSpins) * 100, 2) . "%`n"
  2190.     } else {
  2191.         report .= "RTP Calculado: N/A`n"
  2192.     }
  2193.     report .= "`n"
  2194.     report .= "--- VOLATILIDAD ---`n"
  2195.     report .= "Racha de giros perdidos más larga: " . longestLosingStreak . "`n"
  2196.     report .= "Giros sin victoria actual: " . rescueSpinsWithoutWin . "`n"
  2197.     report .= "Victorias por compensación: " . detailedCompensationCount . " (" . detailedCompensations . " pesos)`n"
  2198.     report .= "`n"
  2199.     report .= "--- JACKPOT ---`n"
  2200.     report .= "Jackpots Ganados: " . detailedJackpotsWon . "`n"
  2201.     report .= "`n"
  2202.     report .= "--- JOKER ---`n"
  2203.     report .= "Respines de Joker: " . detailedJokerRespines . "`n"
  2204.     report .= "Victorias por Respin: " . detailedJokerRespinWins . "`n"
  2205.     report .= "Pesos ganados por Respin: " . detailedJokerRespinPesos . "`n"
  2206.     report .= "Contribución RTP del Joker: " . jokerRTPContribution . "%`n"
  2207.     report .= "`n"
  2208.     report .= "--- SIMBOLOS (VICTORIAS & RTP) ---`n"
  2209.     report .= "`t" . "Simbolo" . "`t" . "Victorias" . "`t" . "Pesos" . "`t" . "RTP Cont.`n"
  2210.     report .= "--------------------------------------------------`n"
  2211.     for symbol, count in detailedWinStats {
  2212.         pesos := detailedPesosStats[symbol]
  2213.         rtp := symbolRTPContributions.Get(symbol, 0.00)
  2214.         report .= "`t" . symbol . "`t`t" . count . "`t" . pesos . "`t" . rtp . "%`n"
  2215.     }
  2216.     return report
  2217. }
  2218.  
  2219. GuiClose(*) {
  2220.     SaveStats()
  2221.     SaveDetailedStats()
  2222.     ExitApp
  2223. }
  2224.  
  2225. PlaySound(file) {
  2226.     global soundPath
  2227.     if FileExist(soundPath . file) {
  2228.         SoundPlay soundPath . file
  2229.     }
  2230. }
Advertisement
Add Comment
Please, Sign In to add comment