package com.example import android.content.Context import android.content.Intent import android.net.Uri import android.os.Bundle import android.provider.Settings import android.widget.Toast import androidx.activity.ComponentActivity import androidx.activity.compose.setContent import androidx.activity.enableEdgeToEdge import androidx.compose.animation.* import androidx.compose.animation.core.* import androidx.compose.foundation.Canvas import androidx.compose.foundation.background import androidx.compose.foundation.border import androidx.compose.foundation.clickable import androidx.compose.foundation.gestures.detectDragGestures import androidx.compose.foundation.layout.* import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.shape.CircleShape import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.verticalScroll import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.* import androidx.compose.material3.* import androidx.compose.runtime.* import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.draw.shadow import androidx.compose.ui.geometry.Offset import androidx.compose.ui.graphics.Brush import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.PathEffect import androidx.compose.ui.graphics.StrokeCap import androidx.compose.ui.input.pointer.pointerInput import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.text.font.FontFamily import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import com.example.ui.theme.MyApplicationTheme import kotlin.math.* class MainActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) enableEdgeToEdge() setContent { MyApplicationTheme { Scaffold( modifier = Modifier.fillMaxSize(), containerColor = Color(0xFF070B13) // Custom premium dark midnight background ) { innerPadding -> AimMasterDashboard( modifier = Modifier.padding(innerPadding), onLaunchGame = { launchCarromPoolDirectly() }, onStartOverlay = { toggleOverlayAssistant(true) }, onStopOverlay = { toggleOverlayAssistant(false) } ) } } } } private fun launchCarromPoolDirectly() { val packageName = "com.miniclip.carrom" val launchIntent = packageManager.getLaunchIntentForPackage(packageName) if (launchIntent != null) { startActivity(launchIntent) Toast.makeText(this, "Launching Carrom Pool...", Toast.LENGTH_SHORT).show() } else { // Not installed, show guide val playStoreIntent = Intent(Intent.ACTION_VIEW, Uri.parse("market://details?id=$packageName")) if (playStoreIntent.resolveActivity(packageManager) != null) { startActivity(playStoreIntent) } else { val browserIntent = Intent(Intent.ACTION_VIEW, Uri.parse("https://play.google.com/store/apps/details?id=$packageName")) startActivity(browserIntent) } Toast.makeText(this, "Carrom Pool is not installed. Directing to Play Store...", Toast.LENGTH_LONG).show() } } private fun toggleOverlayAssistant(start: Boolean) { if (start) { if (!Settings.canDrawOverlays(this)) { val intent = Intent( Settings.ACTION_MANAGE_OVERLAY_PERMISSION, Uri.parse("package:$packageName") ) startActivity(intent) Toast.makeText(this, "Please grant Overlay permission to enable Aim Assist lines.", Toast.LENGTH_LONG).show() } else { val intent = Intent(this, OverlayService::class.java) if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) { startForegroundService(intent) } else { startService(intent) } Toast.makeText(this, "Smart Aim Assist Overlay Started!", Toast.LENGTH_SHORT).show() } } else { stopService(Intent(this, OverlayService::class.java)) Toast.makeText(this, "Aim Assist Stopped.", Toast.LENGTH_SHORT).show() } } fun isUsageStatsGranted(context: Context): Boolean { val appOps = context.getSystemService(Context.APP_OPS_SERVICE) as? android.app.AppOpsManager ?: return false val mode = if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.Q) { appOps.unsafeCheckOpNoThrow( android.app.AppOpsManager.OPSTR_GET_USAGE_STATS, android.os.Process.myUid(), context.packageName ) } else { @Suppress("DEPRECATION") appOps.checkOpNoThrow( android.app.AppOpsManager.OPSTR_GET_USAGE_STATS, android.os.Process.myUid(), context.packageName ) } return mode == android.app.AppOpsManager.MODE_ALLOWED } } fun Context.findActivity(): androidx.activity.ComponentActivity? { var currentContext = this while (currentContext is android.content.ContextWrapper) { if (currentContext is androidx.activity.ComponentActivity) { return currentContext } currentContext = currentContext.baseContext } return null } @Composable fun AimMasterDashboard( modifier: Modifier = Modifier, onLaunchGame: () -> Unit, onStartOverlay: () -> Unit, onStopOverlay: () -> Unit ) { val context = LocalContext.current val mainActivity = context.findActivity() as? MainActivity var hasOverlayPermission by remember { mutableStateOf(Settings.canDrawOverlays(context)) } var hasUsageStats by remember { mutableStateOf(mainActivity?.isUsageStatsGranted(context) ?: true) } var activeTab by remember { mutableStateOf(0) } // 0: Controllers, 1: Trajectory Sandbox // Periodic check for permission on window focus LaunchedEffect(Unit) { while (true) { hasOverlayPermission = Settings.canDrawOverlays(context) hasUsageStats = mainActivity?.isUsageStatsGranted(context) ?: true kotlinx.coroutines.delay(1500) } } Column( modifier = modifier .fillMaxSize() .background(Color(0xFF070B13)) ) { // App Header Column( modifier = Modifier .fillMaxWidth() .background( Brush.verticalGradient( colors = listOf(Color(0xFF0F172A), Color(0xFF070B13)) ) ) .padding(horizontal = 20.dp, vertical = 16.dp) ) { Row( verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.SpaceBetween, modifier = Modifier.fillMaxWidth() ) { Column { Text( text = "AimMaster Carrom", color = Color(0xFF00E5FF), fontSize = 24.sp, fontWeight = FontWeight.Black, fontFamily = FontFamily.Monospace ) Text( text = "Surgical Guideline System", color = Color.LightGray, fontSize = 12.sp, letterSpacing = 1.sp ) } // Connection signal badge Box( modifier = Modifier .clip(RoundedCornerShape(12.dp)) .background(Color(0xFF1E293B)) .padding(horizontal = 10.dp, vertical = 6.dp) ) { Row( verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.spacedBy(4.dp) ) { Box( modifier = Modifier .size(8.dp) .clip(CircleShape) .background(Color(0xFF10B981)) ) Text( text = "COMPAT", color = Color(0xFF10B981), fontSize = 10.sp, fontWeight = FontWeight.Bold ) } } } Spacer(modifier = Modifier.height(16.dp)) // Navigation tabs Row( modifier = Modifier .fillMaxWidth() .clip(RoundedCornerShape(12.dp)) .background(Color(0xFF111827)) .padding(4.dp) ) { TabButton( label = "Control Panel", isActive = activeTab == 0, modifier = Modifier.weight(1f), onClick = { activeTab = 0 } ) TabButton( label = "Aim Trainer Sandbox", isActive = activeTab == 1, modifier = Modifier.weight(1f), onClick = { activeTab = 1 } ) } } Divider(color = Color(0xFF1E293B), thickness = 1.dp) // Contents if (activeTab == 0) { ControlModule( hasOverlayPermission = hasOverlayPermission, onGrantPermission = { val intent = Intent( Settings.ACTION_MANAGE_OVERLAY_PERMISSION, Uri.parse("package:${context.packageName}") ) context.startActivity(intent) }, hasUsageStats = hasUsageStats, onGrantUsageStats = { try { val intent = Intent(Settings.ACTION_USAGE_ACCESS_SETTINGS) context.startActivity(intent) Toast.makeText(context, "Locate 'AimMaster for Carrom' and toggle authorization.", Toast.LENGTH_LONG).show() } catch (e: Exception) { try { val intent = Intent(Settings.ACTION_USAGE_ACCESS_SETTINGS) context.startActivity(intent) } catch (e2: Exception) { Toast.makeText(context, "Please enable Usage Access in system Settings.", Toast.LENGTH_LONG).show() } } }, onLaunchGame = onLaunchGame, onStartOverlay = onStartOverlay, onStopOverlay = onStopOverlay ) } else { TrajectoryTrainerModule() } } } @Composable fun TabButton( label: String, isActive: Boolean, modifier: Modifier = Modifier, onClick: () -> Unit ) { val backgroundBrush = if (isActive) { Brush.linearGradient( colors = listOf(Color(0xFF00E5FF).copy(alpha = 0.15f), Color(0xFFFF2A7A).copy(alpha = 0.1f)) ) } else { Brush.linearGradient(colors = listOf(Color.Transparent, Color.Transparent)) } Box( contentAlignment = Alignment.Center, modifier = modifier .clip(RoundedCornerShape(8.dp)) .background(backgroundBrush) .border( 1.dp, if (isActive) Color(0xFF00E5FF).copy(alpha = 0.4f) else Color.Transparent, RoundedCornerShape(8.dp) ) .clickable { onClick() } .padding(vertical = 10.dp) ) { Text( text = label, color = if (isActive) Color.White else Color.Gray, fontWeight = if (isActive) FontWeight.Bold else FontWeight.Medium, fontSize = 13.sp ) } } @Composable fun ControlModule( hasOverlayPermission: Boolean, onGrantPermission: () -> Unit, hasUsageStats: Boolean, onGrantUsageStats: () -> Unit, onLaunchGame: () -> Unit, onStartOverlay: () -> Unit, onStopOverlay: () -> Unit ) { var reboundState by OverlayService.enableRebound var autoAimState by OverlayService.enableAutoAim var safeModeState by OverlayService.enableSafeMode var autopilotState by OverlayService.enableAutopilot Column( modifier = Modifier .fillMaxSize() .verticalScroll(rememberScrollState()) .padding(16.dp), verticalArrangement = Arrangement.spacedBy(16.dp) ) { // Overlay Permission Block Card( shape = RoundedCornerShape(16.dp), colors = CardDefaults.cardColors(containerColor = Color(0xFF0F172A)), modifier = Modifier .fillMaxWidth() .border( 1.dp, if (hasOverlayPermission) Color(0xFF10B981).copy(alpha = 0.3f) else Color(0xFFEF4444).copy(alpha = 0.3f), RoundedCornerShape(16.dp) ) ) { Row( modifier = Modifier.padding(16.dp), verticalAlignment = Alignment.CenterVertically ) { Icon( imageVector = if (hasOverlayPermission) Icons.Default.CheckCircle else Icons.Default.Warning, contentDescription = "Permission Status", tint = if (hasOverlayPermission) Color(0xFF10B981) else Color(0xFFEF4444), modifier = Modifier.size(36.dp) ) Spacer(modifier = Modifier.width(16.dp)) Column(modifier = Modifier.weight(1f)) { Text( text = if (hasOverlayPermission) "Overlay Active" else "Screen Assist Permission", color = Color.White, fontSize = 15.sp, fontWeight = FontWeight.Bold ) Text( text = if (hasOverlayPermission) "System-level smart guide is ready to overlay on com.miniclip.carrom" else "Draw over apps is required to visualize direct lines and master advanced rebounds during live play.", color = Color.LightGray, fontSize = 11.sp ) } if (!hasOverlayPermission) { Spacer(modifier = Modifier.width(8.dp)) Button( onClick = onGrantPermission, colors = ButtonDefaults.buttonColors(containerColor = Color(0xFFEF4444)) ) { Text("Grant", fontSize = 11.sp) } } } } // Smart Game Tracking block (Highly recommended but optional fallback) Card( shape = RoundedCornerShape(16.dp), colors = CardDefaults.cardColors(containerColor = Color(0xFF0F172A)), modifier = Modifier .fillMaxWidth() .border( 1.dp, if (hasUsageStats) Color(0xFF10B981).copy(alpha = 0.3f) else Color(0xFFF59E0B).copy(alpha = 0.3f), RoundedCornerShape(16.dp) ) ) { Row( modifier = Modifier.padding(16.dp), verticalAlignment = Alignment.CenterVertically ) { Icon( imageVector = if (hasUsageStats) Icons.Default.CheckCircle else Icons.Default.Info, contentDescription = "Usage Status", tint = if (hasUsageStats) Color(0xFF10B981) else Color(0xFFF59E0B), modifier = Modifier.size(36.dp) ) Spacer(modifier = Modifier.width(16.dp)) Column(modifier = Modifier.weight(1f)) { Text( text = "Auto-Hide Game Tracker", color = Color.White, fontSize = 15.sp, fontWeight = FontWeight.Bold ) Text( text = if (hasUsageStats) "Active tracker enabled. Overlay automatically hides when Carrom Pool is inactive." else "Provides seamless automated auto-hide when you exit external menus or game play. Recommended but optional.", color = Color.LightGray, fontSize = 11.sp ) } if (!hasUsageStats) { Spacer(modifier = Modifier.width(8.dp)) Button( onClick = onGrantUsageStats, colors = ButtonDefaults.buttonColors(containerColor = Color(0xFFF59E0B)) ) { Text("Setup", fontSize = 11.sp, color = Color.Black) } } } } // Surgical Features Block Card( shape = RoundedCornerShape(16.dp), colors = CardDefaults.cardColors(containerColor = Color(0xFF111827)), modifier = Modifier.fillMaxWidth() ) { Column( modifier = Modifier.padding(16.dp) ) { Text( text = "Smart Trajectory Settings", color = Color.White, fontSize = 15.sp, fontWeight = FontWeight.Bold, modifier = Modifier.padding(bottom = 12.dp) ) // Rebound Lines SettingToggleCard( title = "Rebound Lines", description = "Visualizes multi-cushion paths off the borders with millimetric reflection calculations.", icon = Icons.Default.TrendingUp, color = Color(0xFF00E5FF), checked = reboundState, onCheckedChange = { reboundState = it } ) Spacer(modifier = Modifier.height(10.dp)) // Auto Aim Assist SettingToggleCard( title = "Auto Aim Assist", description = "Locks onto target vectors automatically. Helps calculate exact collision deflection angle.", icon = Icons.Default.GpsFixed, color = Color(0xFFFF2A7A), checked = autoAimState, onCheckedChange = { autoAimState = it } ) Spacer(modifier = Modifier.height(10.dp)) // Safe Mode SettingToggleCard( title = "Safe Anti-Cheat/Screen Mask", description = "Lower guideline opacities, hides labels, and masks the overlays from streaming filters to guard privacy.", icon = Icons.Default.Security, color = Color(0xFF10B981), checked = safeModeState, onCheckedChange = { safeModeState = it } ) Spacer(modifier = Modifier.height(10.dp)) // Autopilot SettingToggleCard( title = "Precision Autopilot Demo", description = "Simulates continuous guide traces in real time, calculating paths for all targets simultaneously.", icon = Icons.Default.PlayArrow, color = Color(0xFFF59E0B), checked = autopilotState, onCheckedChange = { autopilotState = it } ) } } // Action Buttons Column( verticalArrangement = Arrangement.spacedBy(10.dp), modifier = Modifier.fillMaxWidth() ) { Button( onClick = { onStartOverlay() onLaunchGame() }, colors = ButtonDefaults.buttonColors( containerColor = Color(0xFF00E5FF) ), shape = RoundedCornerShape(14.dp), modifier = Modifier .fillMaxWidth() .height(54.dp) ) { Row( verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.spacedBy(8.dp) ) { Icon(Icons.Default.PlayArrow, contentDescription = "Play", tint = Color.Black) Text( text = "START SMART OVERLAY GUIDE", color = Color.Black, fontWeight = FontWeight.Black, fontSize = 14.sp, letterSpacing = 1.sp ) } } Row( horizontalArrangement = Arrangement.spacedBy(8.dp), modifier = Modifier.fillMaxWidth() ) { OutlinedButton( onClick = onStopOverlay, shape = RoundedCornerShape(12.dp), colors = ButtonDefaults.outlinedButtonColors(contentColor = Color(0xFFEF4444)), modifier = Modifier .weight(1f) .height(48.dp) ) { Text("Stop Guide Lines", fontSize = 11.sp, fontWeight = FontWeight.Bold) } OutlinedButton( onClick = onLaunchGame, shape = RoundedCornerShape(12.dp), colors = ButtonDefaults.outlinedButtonColors(contentColor = Color.LightGray), modifier = Modifier .weight(1f) .height(48.dp) ) { Text("Open Carrom Pool", fontSize = 11.sp, fontWeight = FontWeight.Bold) } } } // App Meta Specs Footer Card( shape = RoundedCornerShape(12.dp), colors = CardDefaults.cardColors(containerColor = Color(0xFF1E293B).copy(alpha = 0.5f)), modifier = Modifier.fillMaxWidth() ) { Column( modifier = Modifier.padding(12.dp), verticalArrangement = Arrangement.spacedBy(4.dp) ) { Text( text = "TRAINING TOOL SPECIFICATIONS:", color = Color(0xFF00E5FF), fontSize = 10.sp, fontWeight = FontWeight.Bold ) Text( text = "• Compatible game version: com.miniclip.carrom (v18.12.1)", color = Color.White, fontSize = 10.sp ) Text( text = "• Min game framework compatibility: v18.5.1", color = Color.White, fontSize = 10.sp ) Text( text = "• Free Aim Overlay Helper — No payment systems included.", color = Color.LightGray, fontSize = 10.sp ) } } } } @Composable fun SettingToggleCard( title: String, description: String, icon: androidx.compose.ui.graphics.vector.ImageVector, color: Color, checked: Boolean, onCheckedChange: (Boolean) -> Unit ) { Row( modifier = Modifier .fillMaxWidth() .clip(RoundedCornerShape(12.dp)) .background(Color(0xFF1E293B).copy(alpha = 0.4f)) .border( 1.dp, if (checked) color.copy(alpha = 0.25f) else Color.Transparent, RoundedCornerShape(12.dp) ) .clickable { onCheckedChange(!checked) } .padding(12.dp), verticalAlignment = Alignment.CenterVertically ) { Icon( imageVector = icon, contentDescription = title, tint = if (checked) color else Color.Gray, modifier = Modifier.size(24.dp) ) Spacer(modifier = Modifier.width(12.dp)) Column(modifier = Modifier.weight(1f)) { Text( text = title, color = if (checked) Color.White else Color.Gray, fontSize = 13.sp, fontWeight = FontWeight.Bold ) Text( text = description, color = Color.LightGray, fontSize = 10.sp, lineHeight = 13.sp ) } Spacer(modifier = Modifier.width(8.dp)) Switch( checked = checked, onCheckedChange = onCheckedChange, colors = SwitchDefaults.colors( checkedThumbColor = Color.Black, checkedTrackColor = color, uncheckedThumbColor = Color.Gray, uncheckedTrackColor = Color.DarkGray ) ) } } // Interactive Board Sandbox Simulator Module @Composable fun TrajectoryTrainerModule() { var strikerPos by remember { mutableStateOf(Offset(200f, 320f)) } var targetPuckPos by remember { mutableStateOf(Offset(200f, 150f)) } var aimAngle by remember { mutableStateOf(-PI.toFloat() / 2) } var reboundState by OverlayService.enableRebound var autoAimState by OverlayService.enableAutoAim var safeModeState by OverlayService.enableSafeMode var autopilotState by OverlayService.enableAutopilot // Physics constants val boardSize = BoardPhysics.BOARD_SIZE val collisionRad = BoardPhysics.COLLISION_RADIUS val pocketRad = BoardPhysics.POCKET_RADIUS // Calculate pocket alignments val closestPocket = remember(targetPuckPos) { BoardPhysics.POCKETS.minBy { pocket -> val dx = targetPuckPos.x - pocket.x val dy = targetPuckPos.y - pocket.y sqrt(dx * dx + dy * dy) } } // Surgical Accuracy: Perfect Contact Point to hit targetPuck in closestPocket val contactPoint = remember(targetPuckPos, closestPocket) { BoardPhysics.getAimAssistImpactVector(strikerPos, targetPuckPos, closestPocket) } // Auto-Aim handler LaunchedEffect(autoAimState, contactPoint, strikerPos) { if (autoAimState) { val dx = contactPoint.x - strikerPos.x val dy = contactPoint.y - strikerPos.y aimAngle = atan2(dy, dx) } } // Autopilot Demo anim state var isFiringAnimation by remember { mutableStateOf(false) } var animatedStrikerX by remember { mutableStateOf(strikerPos.x) } var animatedStrikerY by remember { mutableStateOf(strikerPos.y) } var animatedPuckX by remember { mutableStateOf(targetPuckPos.x) } var animatedPuckY by remember { mutableStateOf(targetPuckPos.y) } LaunchedEffect(isFiringAnimation) { if (isFiringAnimation) { // Simulated physical shot toward the contact point val vx = cos(aimAngle) val vy = sin(aimAngle) // Step 1: Striker slides forward var t = 0f while (t < 1f) { animatedStrikerX = strikerPos.x + vx * t * 120f animatedStrikerY = strikerPos.y + vy * t * 120f t += 0.05f kotlinx.coroutines.delay(16) } // Collide with puck animatedPuckX = targetPuckPos.x animatedPuckY = targetPuckPos.y // Step 2: Puck flies towards closest pocket val pdx = closestPocket.x - targetPuckPos.x val pdy = closestPocket.y - targetPuckPos.y val dist = sqrt(pdx * pdx + pdy * pdy) val divisor = if (dist < 1f) 1f else dist var pT = 0f while (pT < 1f) { animatedPuckX = targetPuckPos.x + (pdx / divisor) * pT * dist animatedPuckY = targetPuckPos.y + (pdy / divisor) * pT * dist pT += 0.08f kotlinx.coroutines.delay(16) } // Pocketed sink animatedPuckX = closestPocket.x animatedPuckY = closestPocket.y kotlinx.coroutines.delay(500) // Reset animatedStrikerX = strikerPos.x animatedStrikerY = strikerPos.y animatedPuckX = targetPuckPos.x animatedPuckY = targetPuckPos.y isFiringAnimation = false } else { animatedStrikerX = strikerPos.x animatedStrikerY = strikerPos.y animatedPuckX = targetPuckPos.x animatedPuckY = targetPuckPos.y } } Column( modifier = Modifier .fillMaxSize() .verticalScroll(rememberScrollState()) .padding(16.dp), horizontalAlignment = Alignment.CenterHorizontally, verticalArrangement = Arrangement.spacedBy(14.dp) ) { // Explanatory badge Text( text = "Aim Trainer Sandbox Sim", color = Color.White, fontSize = 16.sp, fontWeight = FontWeight.Bold, modifier = Modifier.fillMaxWidth(), textAlign = TextAlign.Start ) Text( text = "Drag the STRIKER (teal) and target PUCK (pink) to test rebounds, Safe-Mode line filters, and precision collision vectors here in the workspace.", color = Color.LightGray, fontSize = 11.sp, lineHeight = 14.sp, modifier = Modifier.fillMaxWidth() ) // Sandbox 2D Carrom Canvas Card Card( shape = RoundedCornerShape(20.dp), colors = CardDefaults.cardColors(containerColor = Color(0xFF0F172A)), modifier = Modifier .size(320.dp) .border(1.5.dp, Color(0xFF1E293B), RoundedCornerShape(20.dp)) ) { Box( modifier = Modifier .fillMaxSize() .padding(8.dp) ) { // Main Carrom board visual design using vector canvas Canvas( modifier = Modifier .fillMaxSize() .background(Color(0xFF080D19)) ) { val scaleX = size.width / boardSize val scaleY = size.height / boardSize // Draw center circle frame drawCircle( color = Color(0xFF1E293B), radius = 45f, center = Offset(size.width / 2, size.height / 2), style = androidx.compose.ui.graphics.drawscope.Stroke(width = 2.5f) ) drawCircle( color = Color(0xFFFF2A7A).copy(alpha = 0.2f), radius = 12f, center = Offset(size.width / 2, size.height / 2) ) // Draw 4 corner pockets BoardPhysics.POCKETS.forEach { pocket -> drawCircle( color = Color(0xFF04060A), radius = pocketRad * scaleX, center = Offset(pocket.x * scaleX, pocket.y * scaleY) ) drawCircle( color = Color(0xFFFF2A7A).copy(alpha = 0.3f), radius = (pocketRad + 4) * scaleX, center = Offset(pocket.x * scaleX, pocket.y * scaleY), style = androidx.compose.ui.graphics.drawscope.Stroke(width = 1f) ) } // Baselines drawLine( color = Color(0xFF1E293B), start = Offset(40f * scaleX, 320f * scaleY), end = Offset(360f * scaleX, 320f * scaleY), strokeWidth = 2f ) // Draw trajectory lines val visualStriker = Offset(animatedStrikerX, animatedStrikerY) val visualPuck = Offset(animatedPuckX, animatedPuckY) val startOffset = Offset(visualStriker.x * scaleX, visualStriker.y * scaleY) val targetOffset = Offset(visualPuck.x * scaleX, visualPuck.y * scaleY) // Direct helper shot vector from Striker to contactPoint val visualContactX = contactPoint.x * scaleX val visualContactY = contactPoint.y * scaleY val contactOffset = Offset(visualContactX, visualContactY) // Draw striker aiming beam if (!isFiringAnimation) { drawLine( color = Color(0xFF00FFCC).copy(alpha = if (safeModeState) 0.25f else 0.85f), start = startOffset, end = if (autoAimState) contactOffset else Offset( startOffset.x + cos(aimAngle) * 150f, startOffset.y + sin(aimAngle) * 150f ), strokeWidth = 2f, cap = StrokeCap.Round, pathEffect = PathEffect.dashPathEffect(floatArrayOf(10f, 8f), 0f) ) // If aiming is in line with target puck, draw predicted bounce val hitsPuck = BoardPhysics.testShotSimulation(strikerPos, aimAngle, targetPuckPos) if (hitsPuck || autoAimState) { // Perfect contact point circle drawCircle( color = Color(0xFF00FFCC).copy(alpha = 0.4f), radius = collisionRad * scaleX, center = contactOffset, style = androidx.compose.ui.graphics.drawscope.Stroke( width = 1.5f, pathEffect = PathEffect.dashPathEffect(floatArrayOf(4f, 4f), 0f) ) ) // Direct Puck heading route toward pocket val pdx = closestPocket.x - visualPuck.x val pdy = closestPocket.y - visualPuck.y val dist = sqrt(pdx * pdx + pdy * pdy) val puckDirectHeading = Offset( targetOffset.x + (pdx / dist) * 120f * scaleX, targetOffset.y + (pdy / dist) * 120f * scaleY ) drawLine( color = Color(0xFFFF2A7A).copy(alpha = if (safeModeState) 0.3f else 0.9f), start = targetOffset, end = puckDirectHeading, strokeWidth = 2.5f, cap = StrokeCap.Round ) // Pointy pocket guide trace arrow line drawCircle( color = Color(0xFFFF2A7A).copy(alpha = 0.4f), center = Offset(closestPocket.x * scaleX, closestPocket.y * scaleY), radius = 8f ) // Rebound bounce calculator if (reboundState) { val puckAngle = atan2(pdy, pdx) val reboundSegments = BoardPhysics.calculateReboundLines( visualPuck, puckAngle, maxBounces = 2 ) for (k in 0 until reboundSegments.size - 1) { drawLine( color = Color(0xFFF59E0B).copy(alpha = if (safeModeState) 0.2f else 0.7f), start = Offset(reboundSegments[k].x * scaleX, reboundSegments[k].y * scaleY), end = Offset(reboundSegments[k+1].x * scaleX, reboundSegments[k+1].y * scaleY), strokeWidth = 2f, pathEffect = PathEffect.dashPathEffect(floatArrayOf(6f, 6f), 0f) ) } } } } // DRAW STRIKER (Teal Disc) drawCircle( color = Color(0xFF00E5FF), radius = collisionRad * scaleX, center = startOffset ) drawCircle( color = Color.Black, radius = (collisionRad - 4f) * scaleX, center = startOffset ) drawCircle( color = Color(0xFF00E5FF).copy(alpha = 0.4f), radius = (collisionRad + 3f) * scaleX, center = startOffset, style = androidx.compose.ui.graphics.drawscope.Stroke(width = 1.5f) ) // DRAW TARGET PUCK (Pink Disc) drawCircle( color = Color(0xFFFF2A7A), radius = collisionRad * scaleX, center = targetOffset ) drawCircle( color = Color.White, radius = 4f * scaleX, center = targetOffset ) } // Interactive touch detectors overlaid on specific positions so user can drag them // Drag Striker Box( modifier = Modifier .offset( x = (animatedStrikerX - 25).dp, y = (animatedStrikerY - 25).dp ) .size(50.dp) .pointerInput(isFiringAnimation) { if (!isFiringAnimation) { detectDragGestures { change, dragAmount -> change.consume() // Constraints: striker remains mostly along bottom baseline or adjustable val newX = (strikerPos.x + dragAmount.x).coerceIn(40f, 360f) val newY = (strikerPos.y + dragAmount.y).coerceIn(240f, 360f) strikerPos = Offset(newX, newY) } } } ) // Drag Puck Box( modifier = Modifier .offset( x = (animatedPuckX - 25).dp, y = (animatedPuckY - 25).dp ) .size(50.dp) .pointerInput(isFiringAnimation) { if (!isFiringAnimation) { detectDragGestures { change, dragAmount -> change.consume() val newX = (targetPuckPos.x + dragAmount.x).coerceIn(30f, 370f) val newY = (targetPuckPos.y + dragAmount.y).coerceIn(30f, 300f) targetPuckPos = Offset(newX, newY) } } } ) } } // Action Sliders for non-Auto Aim tweaking if (!autoAimState) { Card( shape = RoundedCornerShape(12.dp), colors = CardDefaults.cardColors(containerColor = Color(0xFF1E293B).copy(alpha = 0.6f)), modifier = Modifier.fillMaxWidth() ) { Column( modifier = Modifier.padding(12.dp) ) { Row( horizontalArrangement = Arrangement.SpaceBetween, modifier = Modifier.fillMaxWidth() ) { Text("Aim Angle Offset", color = Color.White, fontSize = 12.sp, fontWeight = FontWeight.Bold) Text("${(aimAngle * 180 / PI).roundToInt()}°", color = Color(0xFF00E5FF), fontSize = 12.sp) } Slider( value = aimAngle, onValueChange = { aimAngle = it }, valueRange = (-PI.toFloat())..(0f), colors = SliderDefaults.colors( thumbColor = Color(0xFF00E5FF), activeTrackColor = Color(0xFF00E5FF) ) ) } } } Row( horizontalArrangement = Arrangement.spacedBy(10.dp), modifier = Modifier.fillMaxWidth() ) { Button( onClick = { isFiringAnimation = true }, modifier = Modifier.weight(1f), colors = ButtonDefaults.buttonColors(containerColor = Color(0xFFFF2A7A)), enabled = !isFiringAnimation ) { Row( verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.spacedBy(6.dp) ) { Icon(Icons.Default.SportsBasketball, contentDescription = "Shoot", modifier = Modifier.size(16.dp)) Text("TRIGGER SHOT", fontSize = 11.sp, fontWeight = FontWeight.Bold) } } Button( onClick = { strikerPos = Offset(200f, 320f) targetPuckPos = Offset(130f, 150f) autoAimState = true }, modifier = Modifier.weight(1f), colors = ButtonDefaults.buttonColors(containerColor = Color(0xFF10B981)), enabled = !isFiringAnimation ) { Row( verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.spacedBy(6.dp) ) { Icon(Icons.Default.GpsFixed, contentDescription = "Auto", modifier = Modifier.size(16.dp)) Text("AUTO LOCK TRICK", fontSize = 11.sp, fontWeight = FontWeight.Bold) } } } } }