Visualisation:
Last active
May 27, 2022 21:02
-
-
Save veiset/49fe3d224b10a75131ce7597ef65eb52 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package org.veiset.tsar.world | |
import com.badlogic.gdx.math.Vector2 | |
import org.veiset.tsar.engine2.utils.component1 | |
import org.veiset.tsar.engine2.utils.component2 | |
import org.veiset.tsar.engine2.utils.x | |
import org.veiset.tsar.world.icon.WorldIcon | |
import kotlin.math.absoluteValue | |
import kotlin.math.cos | |
import kotlin.math.pow | |
import kotlin.math.sin | |
import kotlin.math.sqrt | |
import kotlin.random.Random | |
data class Universe( | |
val seed: Long, | |
val numberOfTiers: Int = 25, | |
val removeRandomConnections: Boolean = true, | |
val chanceToConnect: Float = 1f, | |
val chanceToConnectSameTier: Float = 0.1f | |
) { | |
private val random = Random(seed) | |
val worlds: List<List<World>> = (0..numberOfTiers).map(::generateTier) | |
init { | |
worlds.forEachIndexed { tierIndex, tier: List<World> -> | |
if (worlds.size - 1 > tierIndex) { | |
val nextTier = worlds[tierIndex + 1] | |
connectEverything(tier, nextTier) | |
if (tierIndex > 2) { | |
if (removeRandomConnections) { | |
removeRandomConnections(tier, tierIndex) | |
} | |
addRandomNeighbourConnections(tier) | |
} | |
} | |
} | |
} | |
private fun connectEverything(tier: List<World>, nextTier: List<World>) { | |
tier.map { world -> | |
world.connections = nextTier | |
.filter { world.withinReach(it) && random.nextFloat() <= chanceToConnect } | |
} | |
} | |
private fun removeRandomConnections(tier: List<World>, tierIndex: Int) { | |
tier.shuffled(random).take(tierIndex).map { | |
it.connections = it.connections.shuffled(random).subList(0, (0..1).random(random)) | |
} | |
} | |
private fun addRandomNeighbourConnections(tier: List<World>) { | |
tier.mapIndexed { mapIndex, world -> | |
val n1 = if (mapIndex - 1 < 0) tier[tier.size - 1] else tier[mapIndex - 1] | |
val n2 = if (mapIndex + 1 > tier.size - 1) tier[0] else tier[mapIndex + 1] | |
if (chanceToConnectSameTier > random.nextFloat()) { | |
n1.connections += world | |
world.connections += n1 | |
} | |
if (chanceToConnectSameTier > random.nextFloat()) { | |
n2.connections += world | |
world.connections += n2 | |
} | |
} | |
} | |
private fun generateTier(tier: Int): List<World> { | |
val maxWorlds = if (tier > 2) tier * 3 + 1 else tier * 2 + 1 | |
val angle = 100f / maxWorlds | |
val halfAngle = (angle / 2.0) | |
return (0..(maxWorlds - 1)) | |
.map { index -> | |
val rn = if (random.nextBoolean()) -1 else 1 | |
val noise = random.nextFloat() | |
val randomAngle = noise * halfAngle * 0.9 * rn | |
val radAngle = Math.toRadians((360f / 100f) * (index * angle + randomAngle)).toFloat() | |
val position = Vector2(tier * cos(radAngle), tier * -sin(radAngle)) | |
World( | |
tier = tier, | |
index = index, | |
size = angle, | |
position = position, | |
seed = random.nextLong(), | |
iconSize = 40 | |
) | |
} | |
} | |
fun worldAtPosition(click: Vector2, radius: Float, size: Float): World? { | |
val xtier = ((click.x / radius).absoluteValue + 0.5) // optimize checking | |
val ytier = ((click.y / radius).absoluteValue + 0.5) // optimize checking | |
val tier = sqrt(xtier.pow(2) + ytier.pow(2)).toInt() | |
return worlds.getOrNull(tier)?.firstOrNull { it.atPosition(click, radius, size) } | |
} | |
} | |
data class World( | |
val tier: Int, | |
val index: Int, | |
val size: Float, | |
val position: Vector2, | |
val seed: Long, | |
val iconSize: Int = 40, | |
var cleared: Boolean = false, | |
val mapSize: Pair<Int, Int> = Pair(60, 40) | |
) { | |
var connections: List<World> = emptyList() | |
val icon = WorldIcon(seed, iconSize) | |
val monsters: Int = 10 + tier + (Random(seed).nextFloat() * 10 * tier).toInt() | |
fun withinReach(world: World): Boolean { | |
val targetPosition = world.size * world.index | |
val pos = index * size | |
val reach = size / 2f | |
if (pos + reach > targetPosition && pos - reach < targetPosition) { | |
return true | |
} | |
if (pos - reach < 0 && 100 - reach < targetPosition) { | |
return true | |
} | |
if (pos + reach > 100 && 100 - pos + reach > targetPosition) { | |
return true | |
} | |
return false | |
} | |
fun atPosition(click: Vector2, scale: Float, iconSize: Float): Boolean { | |
val (x, y) = (this.position.x * scale x this.position.y * scale) | |
val offset = iconSize / 2 | |
return click.x in (x - offset..x + offset) && click.y in (y - offset..y + offset) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment