ceska12

script

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