Auto-commit after successful build: update app gameplay/UI, improve build setup
Changed files:\napp/build.gradle.kts app/src/main/java/solutions/tretter/githugandroid/GameModels.kt app/src/main/java/solutions/tretter/githugandroid/GitHelpCommands.kt app/src/main/java/solutions/tretter/githugandroid/GitHugApp.kt app/src/main/java/solutions/tretter/githugandroid/GitRuntime.kt app/src/main/java/solutions/tretter/githugandroid/Visual.kt app/src/main/java/solutions/tretter/githugandroid/levels/AdvancedLevels.kt app/src/main/java/solutions/tretter/githugandroid/levels/LevelCatalog.kt app/src/test/java/solutions/tretter/githugandroid/GitHelpCommandsTest.kt app/src/test/java/solutions/tretter/githugandroid/GitSandboxEngineTest.kt
This commit is contained in:
@@ -1,7 +1,15 @@
|
||||
package solutions.tretter.githugandroid
|
||||
|
||||
import androidx.compose.animation.core.LinearEasing
|
||||
import androidx.compose.animation.core.RepeatMode
|
||||
import androidx.compose.animation.core.animateFloat
|
||||
import androidx.compose.animation.core.infiniteRepeatable
|
||||
import androidx.compose.animation.core.rememberInfiniteTransition
|
||||
import androidx.compose.animation.core.tween
|
||||
import androidx.compose.foundation.Canvas
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
@@ -10,10 +18,12 @@ import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.imePadding
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.Surface
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.collectAsState
|
||||
@@ -23,11 +33,16 @@ import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.geometry.Offset
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.platform.LocalConfiguration
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.text.input.TextFieldValue
|
||||
import androidx.compose.ui.text.TextRange
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
@Composable
|
||||
@@ -63,8 +78,16 @@ fun GitHugApp() {
|
||||
var editorState by remember { mutableStateOf<TextEditorState?>(null) }
|
||||
var gitMessageEditorState by remember { mutableStateOf<GitMessageEditorState?>(null) }
|
||||
var manPageState by remember { mutableStateOf<ManPageState?>(null) }
|
||||
var solvedCelebrationTitle by remember { mutableStateOf<String?>(null) }
|
||||
val currentLevel = levels[currentLevelIndex]
|
||||
|
||||
LaunchedEffect(solvedCelebrationTitle) {
|
||||
if (solvedCelebrationTitle != null) {
|
||||
delay(3_000)
|
||||
solvedCelebrationTitle = null
|
||||
}
|
||||
}
|
||||
|
||||
LaunchedEffect(persistedPaneLayout) {
|
||||
paneLayout = persistedPaneLayout
|
||||
}
|
||||
@@ -179,11 +202,11 @@ fun GitHugApp() {
|
||||
applyRecommendedPaneWeights(persist = false)
|
||||
}
|
||||
|
||||
fun loadLevel(index: Int) {
|
||||
fun loadLevel(index: Int, message: List<String> = listOf("Loaded level: ${levels[index].title}")) {
|
||||
AppLog.d("GitHugApp", "Loading level index=$index id=${levels[index].id} title=${levels[index].title}")
|
||||
currentLevelIndex = index
|
||||
repo = runtime.prepareLevel(levels[index])
|
||||
output = listOf("Loaded level: ${levels[index].title}")
|
||||
output = message
|
||||
clearCommandInput()
|
||||
suppressedImeEcho = null
|
||||
hintIndex = 0
|
||||
@@ -275,12 +298,15 @@ fun GitHugApp() {
|
||||
}
|
||||
addAll(lines)
|
||||
if (solvedAfterCommand && !wasAlreadyCompleted) {
|
||||
add("✔ Level solved: ${levelForResult.title}")
|
||||
add("")
|
||||
add("👍 Level solved: ${levelForResult.title}")
|
||||
add("Nice. ${completedLevels.size + 1}/${levels.size} levels complete.")
|
||||
}
|
||||
}
|
||||
if (solvedAfterCommand && !wasAlreadyCompleted) {
|
||||
val updatedCompletedLevels = completedLevels + levelForResult.id
|
||||
completedLevels = updatedCompletedLevels
|
||||
solvedCelebrationTitle = levelForResult.title
|
||||
val nextLevelIndex = levels.indexOfFirst { it.id !in updatedCompletedLevels }
|
||||
if (nextLevelIndex >= 0) {
|
||||
val nextLevelId = levels[nextLevelIndex].id
|
||||
@@ -289,12 +315,17 @@ fun GitHugApp() {
|
||||
"Level solved ${levelForResult.id}; advancing to next incomplete index=$nextLevelIndex id=$nextLevelId",
|
||||
)
|
||||
scope.launch { persistProgress(updatedCompletedLevels, nextLevelId) }
|
||||
loadLevel(nextLevelIndex)
|
||||
loadLevel(
|
||||
nextLevelIndex,
|
||||
message = newOutput + listOf(
|
||||
"Next level loaded: ${levels[nextLevelIndex].title}",
|
||||
),
|
||||
)
|
||||
} else {
|
||||
AppLog.d("GitHugApp", "All levels completed")
|
||||
scope.launch { persistProgress(updatedCompletedLevels, levels.lastOrNull()?.id) }
|
||||
repo = newRepo
|
||||
output = listOf("🏁 All Githug levels completed.")
|
||||
output = newOutput + listOf("🏁 All Githug levels completed.")
|
||||
clearCommandInput()
|
||||
suppressedImeEcho = null
|
||||
}
|
||||
@@ -427,10 +458,10 @@ fun GitHugApp() {
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.fillMaxSize()
|
||||
.background(AppBackground)
|
||||
.verticalScroll(screenScrollState)
|
||||
.imePadding()
|
||||
.verticalScroll(screenScrollState)
|
||||
.padding(horizontal = 10.dp, vertical = 8.dp),
|
||||
verticalArrangement = Arrangement.spacedBy(8.dp),
|
||||
) {
|
||||
@@ -537,5 +568,55 @@ fun GitHugApp() {
|
||||
onClose = { manPageState = null },
|
||||
)
|
||||
}
|
||||
|
||||
solvedCelebrationTitle?.let { title ->
|
||||
SolvedCelebrationOverlay(title = title)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun SolvedCelebrationOverlay(title: String) {
|
||||
val transition = rememberInfiniteTransition(label = "solved-celebration")
|
||||
val progress by transition.animateFloat(
|
||||
initialValue = 0f,
|
||||
targetValue = 1f,
|
||||
animationSpec = infiniteRepeatable(
|
||||
animation = tween(durationMillis = 1_200, easing = LinearEasing),
|
||||
repeatMode = RepeatMode.Restart,
|
||||
),
|
||||
label = "confetti-progress",
|
||||
)
|
||||
val colors = listOf(Accent, Success, Color(0xFFFFD166), Color(0xFFFF6B6B), Color(0xFF9BF6FF))
|
||||
|
||||
Box(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
contentAlignment = Alignment.Center,
|
||||
) {
|
||||
Canvas(modifier = Modifier.fillMaxSize()) {
|
||||
repeat(42) { index ->
|
||||
val x = ((index * 73) % 100) / 100f * size.width
|
||||
val baseY = ((index * 37) % 100) / 100f * size.height
|
||||
val y = (baseY + progress * size.height * 0.6f) % size.height
|
||||
val pieceSize = 6.dp.toPx() + (index % 4) * 2.dp.toPx()
|
||||
drawCircle(
|
||||
color = colors[index % colors.size],
|
||||
radius = pieceSize / 2f,
|
||||
center = Offset(x, y),
|
||||
alpha = 0.85f,
|
||||
)
|
||||
}
|
||||
}
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.background(PanelPrimary.copy(alpha = 0.94f), RoundedCornerShape(24.dp))
|
||||
.padding(horizontal = 26.dp, vertical = 22.dp),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
verticalArrangement = Arrangement.spacedBy(8.dp),
|
||||
) {
|
||||
Text(text = "👍", fontSize = 56.sp)
|
||||
Text(text = "Level solved", color = Success, fontSize = 22.sp)
|
||||
Text(text = title, color = TextSecondary, fontSize = 14.sp)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user