--- a/settings.gradle +++ b/settings.gradle @@ -1,10 +1,11 @@ pluginManagement { + repositories { + mavenCentral() + gradlePluginPortal() + } plugins { id 'org.jetbrains.kotlin.jvm' version "$kotlinVersion" } } -plugins { - id 'org.gradle.toolchains.foojay-resolver-convention' version '1.0.0' -} -include 'core', 'lwjgl3', 'android', 'ios' +include 'core', 'teavm' --- a/build.gradle +++ b/build.gradle @@ -1,6 +1,5 @@ buildscript { repositories { - mavenLocal() mavenCentral() google() gradlePluginPortal() @@ -8,11 +7,7 @@ maven { url = 'https://jitpack.io' } } dependencies { - //classpath "com.android.tools.build:gradle:$gradleVersion" - // AGP Update Assistant needs this version numbers here, not in gradle.properties - classpath 'com.android.tools.build:gradle:9.1.1' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion" - classpath "com.mobidevelop.robovm:robovm-gradle-plugin:$roboVMVersion" } } @@ -24,7 +19,6 @@ version = '1.17' ext.appName = 'Tangler' repositories { - mavenLocal() mavenCentral() google() gradlePluginPortal() --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -4,3 +4,4 @@ distributionUrl=https\://services.gradle.org/distributions/gradle-9.7.1-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists +distributionSha256Sum=acd53f1edaf02f1a8ff99879f8a34b302661a057d9b063ae9e35b552f804d20a --- /dev/null +++ b/teavm/build.gradle @@ -0,0 +1,28 @@ +import org.teavm.gradle.api.OptimizationLevel +plugins { + id 'java' + id 'com.github.xpenatan.gdx-teavm' version '1.6.2' +} + +java { + sourceCompatibility = JavaVersion.VERSION_17 + targetCompatibility = JavaVersion.VERSION_17 +} + +dependencies { + implementation "com.badlogicgames.gdx:gdx:$gdxVersion" + implementation "com.github.xpenatan.gdx-teavm:gdx-freetype-web:1.6.2" + implementation project(':core') +} + +gdxTeaVM { + assets.from(file('../assets')) + + js { + mainClass.set('com.andrzejn.tangler.teavm.WebLauncher') + htmlTitle.set('Tangler') + obfuscated.set(false) + // NONE: with BALANCED the order of some classes in app.js changes from one build to the next + optimization.set(OptimizationLevel.NONE) + } +} --- /dev/null +++ b/teavm/src/main/java/com/andrzejn/tangler/teavm/WebLauncher.java @@ -0,0 +1,19 @@ +package com.andrzejn.tangler.teavm; + +import com.andrzejn.tangler.Main; +import com.github.xpenatan.gdx.teavm.backends.web.WebApplication; +import com.github.xpenatan.gdx.teavm.backends.web.WebApplicationConfiguration; + +/** Launches the browser (TeaVM) build. */ +public class WebLauncher { + public static void main(String[] args) { + WebApplicationConfiguration config = new WebApplicationConfiguration("canvas"); + config.width = 0; + config.height = 0; + // every game of this site runs on the same origin: keep this game's preferences and saved files apart + config.storagePrefix = "tangler"; + config.localStoragePrefix = "fredygames/tangler"; + config.preloadListener = assetLoader -> assetLoader.loadScript("freetype.js"); + new WebApplication(new Main(), config); + } +} --- /dev/null +++ b/core/src/main/kotlin/com/andrzejn/tangler/helper/Random.kt @@ -0,0 +1,12 @@ +package com.andrzejn.tangler.helper + +/** + * Drop-in replacement for kotlin.random.Random.Default. The JVM implementation of the default generator delegates to + * java.util.concurrent.ThreadLocalRandom, which TeaVM (the browser build) cannot create: its setSeed() throws + * UnsupportedOperationException. This one is the plain Kotlin generator seeded from the clock. + */ +object Random : kotlin.random.Random() { + private val impl = kotlin.random.Random(System.nanoTime()) + + override fun nextBits(bitCount: Int): Int = impl.nextBits(bitCount) +} --- a/core/src/main/kotlin/com/andrzejn/tangler/helper/Atlas.kt +++ b/core/src/main/kotlin/com/andrzejn/tangler/helper/Atlas.kt @@ -38,7 +38,7 @@ * It is more convenient than keep a lot of fixed font bitmaps for different resolutions. */ fun createFont(height: Int): BitmapFont { - with(FreeTypeFontGenerator(Gdx.files.internal("ADYS-Bold_V5.ttf"))) { + with(FreeTypeFontGenerator(Gdx.files.internal("Poppins-Bold.ttf"))) { setMaxTextureSize(2048) // Required for same devices like Xiaomi, where the default 1024 causes garbled fonts val font = generateFont(FreeTypeFontGenerator.FreeTypeFontParameter().apply { size = height --- a/core/src/main/kotlin/com/andrzejn/tangler/logic/PlayField.kt +++ b/core/src/main/kotlin/com/andrzejn/tangler/logic/PlayField.kt @@ -1,6 +1,6 @@ package com.andrzejn.tangler.logic -import kotlin.random.Random +import com.andrzejn.tangler.helper.Random /** * The logical playfield. Contains the cells grid and provides the game back-end logic that is not tied --- a/core/src/main/kotlin/com/andrzejn/tangler/logic/Tile.kt +++ b/core/src/main/kotlin/com/andrzejn/tangler/logic/Tile.kt @@ -1,6 +1,6 @@ package com.andrzejn.tangler.logic -import kotlin.random.Random +import com.andrzejn.tangler.helper.Random /** * Tile contains one or several colored segments. Until put to a field cell, the tile can be rotated. After putting @@ -61,9 +61,9 @@ * Generate and add to the tile random number of random segments, taking into account the game settings. */ fun createRandomSegments(segmentsCount: Int = randomNumberOfSegments()) { - val sides = (0 until sidesCount).shuffled() + val sides = (0 until sidesCount).shuffled(Random) // The sides list contains randomized sequence of cell sides, to use in the generated segments - val colors = (if (allowDuplicateColors) (1..colorsCount) else emptyList()).plus((1..colorsCount)).shuffled() + val colors = (if (allowDuplicateColors) (1..colorsCount) else emptyList()).plus((1..colorsCount)).shuffled(Random) // The colors list contains randomized sequence of all available colors, to use in the generated segments var j = 0 for (i in 0 until segmentsCount) { @@ -174,7 +174,7 @@ val segmentStart = indexOfRandomOrNull(borderColors) { (bc, _) -> bc.color == 0 && bc.nonBlockingOptions.size > 0 } if (segmentStart != null) { - val segmentColor = borderColors[segmentStart].nonBlockingOptions.random() + val segmentColor = borderColors[segmentStart].nonBlockingOptions.random(Random) borderColors[segmentStart].nonBlockingOptions.remove(segmentColor) val segmentEnd = indexOfRandomOrNull(borderColors) { (bc, _) -> bc.color == 0 && bc.nonBlockingOptions.contains(segmentColor) @@ -196,7 +196,7 @@ borderColors: Array, filterBy: (Pair) -> Boolean ) = - borderColors.mapIndexed { i, bc -> bc to i }.filter(filterBy).randomOrNull()?.second + borderColors.mapIndexed { i, bc -> bc to i }.filter(filterBy).randomOrNull(Random)?.second /** * Copies segments from other tile. Used to save/restore last move. --- a/core/src/main/kotlin/com/andrzejn/tangler/screens/BaseGameboard.kt +++ b/core/src/main/kotlin/com/andrzejn/tangler/screens/BaseGameboard.kt @@ -21,7 +21,7 @@ import com.badlogic.gdx.math.Vector2 import kotlin.math.abs import kotlin.math.min -import kotlin.random.Random +import com.andrzejn.tangler.helper.Random /** * Grid lines width. Used in some other places to draw lines. --- a/core/src/main/kotlin/com/andrzejn/tangler/screens/CreditsScreen.kt +++ b/core/src/main/kotlin/com/andrzejn/tangler/screens/CreditsScreen.kt @@ -40,9 +40,6 @@ private var gridX = 0f private var gridY = 0f private val logo = Sprite(ctx.a.logo) - private val icongmail = Sprite(ctx.a.icongmail) - private val icontelegram = Sprite(ctx.a.icontelegram) - private val icongithub = Sprite(ctx.a.icongithub) private val exit = Sprite(ctx.a.exit) private val settings = Sprite(ctx.a.settings) @@ -61,21 +58,6 @@ (ctx.viewportWidth - logo.width) / 2, gridY * 8 - logo.height / 2 ) - ctx.fitToRect(icongmail, gridX * 0.9f, gridY * 0.9f) - icongmail.setPosition( - gridX - icongmail.width / 2, - gridY * 5 + (gridY - icongmail.height) / 2 - ) - ctx.fitToRect(icontelegram, gridX * 0.9f, gridY * 0.9f) - icontelegram.setPosition( - gridX - icontelegram.width / 2, - gridY * 4 + (gridY - icontelegram.height) / 2 - ) - ctx.fitToRect(icongithub, gridX * 0.9f, gridY * 0.9f) - icongithub.setPosition( - gridX - icongithub.width / 2, - gridY * 3 + (gridY - icongithub.height) / 2 - ) ctx.fitToRect(exit, gridX * 0.9f, gridY * 0.9f) exit.setPosition( 7 * gridX - exit.width / 2, @@ -87,11 +69,13 @@ (gridY - settings.height) / 2 ) font.dispose() - font = ctx.a.createFont((icongmail.height * 0.5).toInt()) + font = ctx.a.createFont((gridY * 0.4f).toInt()) fcText = BitmapFontCache(font) - fcText.setText("andrzej.novosiolov@gmail.com", gridX * 1.7f, gridY * 5.5f, gridX * 5f, Align.left, false) - fcText.addText("t.me/Andrzejn", gridX * 1.7f, gridY * 4.5f, gridX * 5f, Align.left, false) - fcText.addText("github.com/andrzej-nov", gridX * 1.7f, gridY * 3.5f, gridX * 5f, Align.left, false) + // Browser build: no mail, Telegram or GitHub links (they were icons plus Gdx.net.openURI calls) + fcText.setText("Tangler by Andrzej Novosiolov", gridX * 0.5f, gridY * 6.5f, gridX * 7f, Align.center, false) + fcText.addText("Licensed under CC BY 4.0", gridX * 0.5f, gridY * 5.5f, gridX * 7f, Align.center, false) + fcText.addText("Icons from flaticon.com", gridX * 0.5f, gridY * 4.5f, gridX * 7f, Align.center, false) + fcText.addText("Web version by FredyGames", gridX * 0.5f, gridY * 3.5f, gridX * 7f, Align.center, false) fcText.setColors(ctx.drw.theme.creditsText) fcTime = BitmapFontCache(font) fcTime.setText(millisToTimeString(ctx.gs.inGameDuration), gridX * 1.5f, gridY, gridX * 5f, Align.center, false) @@ -121,9 +105,6 @@ ctx.batch.begin() ctx.drw.sd.filledRectangle(0f, 0f, ctx.viewportWidth, ctx.viewportHeight, ctx.drw.theme.screenBackground) logo.draw(ctx.batch) - icongmail.draw(ctx.batch) - icontelegram.draw(ctx.batch) - icongithub.draw(ctx.batch) settings.draw(ctx.batch) exit.draw(ctx.batch) fcText.draw(ctx.batch) @@ -141,13 +122,7 @@ override fun touchDown(screenX: Int, screenY: Int, pointer: Int, button: Int): Boolean { val v = ctx.drw.pointerPositionScreen(Gdx.input.x, Gdx.input.y) - if (v.y in 5f * gridY..6f * gridY) - Gdx.net.openURI("mailto:andrzej.novosiolov@gmail.com?subject=The%20Tangler%20game") - else if (v.y in 4f * gridY..5f * gridY) - Gdx.net.openURI("https://t.me/AndrzejN") - else if (v.y in 3f * gridY..4f * gridY) - Gdx.net.openURI("https://github.com/andrzej-nov/Tangler") - else if (v.y < gridY) { + if (v.y < gridY) { if (v.x < gridX * 2) ctx.game.setScreen() else if (v.x > 6 * gridX)