diff --git a/assets/mods/basegame/blocks/blocks.csv b/assets/mods/basegame/blocks/blocks.csv index 77741f373..94da7a749 100644 --- a/assets/mods/basegame/blocks/blocks.csv +++ b/assets/mods/basegame/blocks/blocks.csv @@ -11,8 +11,8 @@ "21";"21";"21";"BLOCK_STONE_MARBLE";"0.1252";"0.1252";"0.1252";"0.1252";"48";"2400";"ROCK";"1";"1";"N/A";"0";"4";"16";"0.0000";"0.0000";"0.0000";"0.0000";"N/A";"N/A";"0.1";"ROCK,NATURAL" # dirts -"32";"32";"32";"BLOCK_DIRT";"0.1252";"0.1252";"0.1252";"0.1252";"24";"1400";"DIRT";"1";"1";"N/A";"0";"4";"16";"0.0000";"0.0000";"0.0000";"0.0000";"N/A";"N/A";"0.0";"DIRT,NATURAL" -"33";"32";"32";"BLOCK_GRASS";"0.1252";"0.1252";"0.1252";"0.1252";"24";"1400";"GRSS";"1";"0";"N/A";"0";"0";"16";"0.0000";"0.0000";"0.0000";"0.0000";"N/A";"N/A";"0.0";"GRASS,NATURAL" +"32";"32";"32";"BLOCK_DIRT";"0.1252";"0.1252";"0.1252";"0.1252";"24";"1400";"DIRT";"1";"1";"N/A";"0";"4";"16";"0.0000";"0.0000";"0.0000";"0.0000";"N/A";"N/A";"0.0";"DIRT,NATURAL,CULTIVABLE" +"33";"32";"32";"BLOCK_GRASS";"0.1252";"0.1252";"0.1252";"0.1252";"24";"1400";"GRSS";"1";"0";"N/A";"0";"0";"16";"0.0000";"0.0000";"0.0000";"0.0000";"N/A";"N/A";"0.0";"GRASS,NATURAL,CULTIVABLE" "34";"34";"34";"BLOCK_GRASSWALL";"0.1252";"0.1252";"0.1252";"0.1252";"24";"1400";"GRSS";"1";"1";"N/A";"0";"0";"16";"0.0000";"0.0000";"0.0000";"0.0000";"N/A";"N/A";"0.0";"GRASS,NATURAL" "35";"item@basegame:25";"item@basegame:25";"BLOCK_CLAY";"0.1252";"0.1252";"0.1252";"0.1252";"24";"1700";"DIRT";"1";"1";"N/A";"0";"4";"16";"0.0000";"0.0000";"0.0000";"0.0000";"N/A";"N/A";"0.0";"DIRT,NATURAL" #"35";"35";"35";"BLOCK_FOLIAGE_GREEN";"0.1252";"0.1252";"0.1252";"0.1252";"24";"1400";"GRSS";"1";"1";"N/A";"0";"0";"16";"0.0000";"0.0000";"0.0000";"0.0000";"N/A";"N/A";"0.0";"GRASS,NATURAL" diff --git a/assets/mods/basegame/sprites/saplings.tga b/assets/mods/basegame/sprites/saplings.tga new file mode 100644 index 000000000..c8bb2ea34 --- /dev/null +++ b/assets/mods/basegame/sprites/saplings.tga @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6066a31fc75f1900c6005967fb69acd5fd59eee4b0bd65796925e08026a8484b +size 32786 diff --git a/src/net/torvald/terrarum/modulebasegame/gameactors/Cultivable.kt b/src/net/torvald/terrarum/modulebasegame/gameactors/Cultivable.kt new file mode 100644 index 000000000..0b71fedf2 --- /dev/null +++ b/src/net/torvald/terrarum/modulebasegame/gameactors/Cultivable.kt @@ -0,0 +1,135 @@ +package net.torvald.terrarum.modulebasegame.gameactors + +import com.badlogic.gdx.Input +import com.badlogic.gdx.graphics.Color +import com.badlogic.gdx.graphics.g2d.SpriteBatch +import net.torvald.spriteanimation.SheetSpriteAnimation +import net.torvald.terrarum.* +import net.torvald.terrarum.App.printdbg +import net.torvald.terrarum.blockproperties.Block +import net.torvald.terrarum.gamecontroller.KeyToggler +import net.torvald.terrarum.gameitems.ItemID +import net.torvald.terrarum.modulebasegame.worldgenerator.Treegen +import net.torvald.terrarumsansbitmap.gdx.TextureRegionPack + +/** + * Created by minjaesong on 2024-02-03. + */ +open class Cultivable: FixtureBase { + + open var currentGrowth = 0f + @Transient var maxGrowth: Int = 0; private set + @Transient open val growthPerTick = 1f + @Transient open val growthRandomness = 0.33333334f + open var growthBonusMult = 1f + + override fun canSpawnOnThisFloor(itemID: ItemID) = BlockCodex[itemID].hasTag("CULTIVABLE") + + constructor() : super( + BlockBox(BlockBox.NO_COLLISION, 1, 2), + nameFun = { " " } + ) + + constructor(maxGrowth: Int) : super( + BlockBox(BlockBox.NO_COLLISION, 1, 2), + nameFun = { " " } + ) { + this.maxGrowth = maxGrowth + } + + fun tickGrowthCounter() { + val worldTimeDelta = INGAME.world.worldTime.timeDelta + val rnd = 1f + (((Math.random() * 2.0) - 1.0) * growthRandomness) + currentGrowth += growthPerTick * worldTimeDelta * growthBonusMult * rnd.toFloat() + } + + open fun tryToSpawnMaturePlant() { + + } + +} + + +/** + * Created by minjaesong on 2024-02-03. + */ +open class SaplingBase(val species: Int) : Cultivable(72000) { + private val variant = (0..3).random() + init { + CommonResourcePool.addToLoadingList("basegame/sprites/saplings.tga") { + TextureRegionPack(ModMgr.getGdxFile("basegame", "sprites/saplings.tga"), 16, 32) + } + CommonResourcePool.loadAll() + + makeNewSprite(CommonResourcePool.getAsTextureRegionPack("basegame/sprites/saplings.tga")).let { + it.setRowsAndFrames(4,4) + } + } + + override fun update(delta: Float) { + super.update(delta) + + // these have to run every frame to make the sprite static + (sprite as SheetSpriteAnimation).currentRow = species + (sprite as SheetSpriteAnimation).currentFrame = variant + + if (!flagDespawn) { + tickGrowthCounter() + +// printdbg(this, "growth=$currentGrowth/$maxGrowth") + + if (currentGrowth >= maxGrowth) { + tryToSpawnMaturePlant() + } + } + } + private var treeHasBeenGrown = false + override fun tryToSpawnMaturePlant() { + if (INGAME.WORLD_UPDATE_TIMER % 3 == 2) { + val size = if (Math.random() < 0.1) 2 else 1 + val result = Treegen.plantTree(INGAME.world, intTilewiseHitbox.startX.toInt(), intTilewiseHitbox.endY.toInt() + 1, species, size) + + if (result) { + treeHasBeenGrown = true + flagDespawn() + } + } + } + + /** + * this function will be called when: + * 1. player removes a sapling that has not yet matured + * 2. the sapling is matured and the tree is about to be spawned + */ + + override fun despawn() { + + if (canBeDespawned) { + printdbg(this, "despawn at T${INGAME.WORLD_UPDATE_TIMER}: ${nameFun()}") +// printStackTrace(this) + + // remove filler block + if (!treeHasBeenGrown) { + forEachBlockbox { x, y, _, _ -> + world!!.setTileTerrain(x, y, Block.AIR, true) + } + } + + worldBlockPos = null + mainUI?.dispose() + + this.isVisible = false + + despawnHook(this) + } + else { + printdbg(this, "failed to despawn at T${INGAME.WORLD_UPDATE_TIMER}: ${nameFun()}") + printdbg(this, "cannot despawn a fixture with non-empty inventory") + } + } +} + +class SaplingOak : SaplingBase(0) +class SaplingEbony : SaplingBase(1) +class SaplingBirch : SaplingBase(2) +class SaplingRosewood : SaplingBase(3) diff --git a/src/net/torvald/terrarum/modulebasegame/gameactors/FixtureBase.kt b/src/net/torvald/terrarum/modulebasegame/gameactors/FixtureBase.kt index 80bc10d5d..d0acb5de8 100644 --- a/src/net/torvald/terrarum/modulebasegame/gameactors/FixtureBase.kt +++ b/src/net/torvald/terrarum/modulebasegame/gameactors/FixtureBase.kt @@ -245,6 +245,13 @@ open class FixtureBase : ActorWithBody, CuedByTerrainChange { } } + /** + * Condition for (if the tile is solid) is always implied regardless of this function. See [canSpawnHere0] + */ + open fun canSpawnOnThisFloor(itemID: ItemID): Boolean { + return true + } + fun canSpawnHere(posX0: Int, posY0: Int): Boolean { val posX = (posX0 - blockBox.width.minus(1).div(2)) fmod world!!.width // width.minus(1) so that spawning position would be same as the ghost's position val posY = posY0 - blockBox.height + 1 @@ -269,7 +276,11 @@ open class FixtureBase : ActorWithBody, CuedByTerrainChange { if (spawnNeedsFloor) { val y = posY + blockBox.height val xs = posX until posX + blockBox.width - cannotSpawn = cannotSpawn or xs.any { x -> !BlockCodex[world!!.getTileFromTerrain(x, y)].isSolid } + cannotSpawn = cannotSpawn or xs.any { x -> + world!!.getTileFromTerrain(x, y).let { + !BlockCodex[it].isSolid || !canSpawnOnThisFloor(it) + } + } } return !cannotSpawn @@ -297,7 +308,8 @@ open class FixtureBase : ActorWithBody, CuedByTerrainChange { val posY = posY0 - blockBox.height + 1 if (!canSpawnHere(posX0, posY0)) { - printdbg(this, "cannot spawn fixture ${nameFun()} at F${INGAME.WORLD_UPDATE_TIMER}, has tile collision; xy=($posX,$posY) tDim=(${blockBox.width},${blockBox.height})") + printdbg(this, "cannot spawn fixture1 ${nameFun()} at F${INGAME.WORLD_UPDATE_TIMER}, has tile collision; xy=($posX,$posY) tDim=(${blockBox.width},${blockBox.height})") + printStackTrace(this) return false } printdbg(this, "spawn fixture ${nameFun()} at F${INGAME.WORLD_UPDATE_TIMER}, xy=($posX,$posY) tDim=(${blockBox.width},${blockBox.height})") @@ -314,6 +326,12 @@ open class FixtureBase : ActorWithBody, CuedByTerrainChange { blockBox.width * TILE_SIZED, blockBox.height * TILE_SIZED ) + this.intTilewiseHitbox.setFromWidthHeight( + posX.toDouble(), + posY.toDouble(), + blockBox.width.toDouble(), + blockBox.height.toDouble() + ) // actually add this actor into the world INGAME.queueActorAddition(this) @@ -399,10 +417,16 @@ open class FixtureBase : ActorWithBody, CuedByTerrainChange { blockBox.width * TILE_SIZED, blockBox.height * TILE_SIZED ) + this.intTilewiseHitbox.setFromWidthHeight( + posX.toDouble(), + posY.toDouble(), + blockBox.width.toDouble(), + blockBox.height.toDouble() + ) // check for existing blocks (and fixtures) if (!canSpawnHere0(posX, posY)) { - printdbg(this, "cannot spawn fixture ${nameFun()} at F${INGAME.WORLD_UPDATE_TIMER}, has tile collision; xy=($posX,$posY) tDim=(${blockBox.width},${blockBox.height})") + printdbg(this, "cannot spawn fixture2 ${nameFun()} at F${INGAME.WORLD_UPDATE_TIMER}, has tile collision; xy=($posX,$posY) tDim=(${blockBox.width},${blockBox.height})") return false } printdbg(this, "spawn fixture ${nameFun()} at F${INGAME.WORLD_UPDATE_TIMER}, xy=($posX,$posY) tDim=(${blockBox.width},${blockBox.height})") @@ -471,21 +495,10 @@ open class FixtureBase : ActorWithBody, CuedByTerrainChange { protected var dropItem = false + /** + * This function MUST BE super-called for make despawn call to work at all. + */ override fun update(delta: Float) { - // FIXME retrieving fixture by mining relied on a quirk that mining a actorblock would also drop the fixture. - // FIXME since that particular method of operation causes so much problems, it is required to implement the - // FIXME said feature "correctly" - /*if (!flagDespawn && worldBlockPos != null) { - // removal-by-player because player is removing the filler block by pick - // no-flagDespawn check is there to prevent item dropping when externally despawned - // (e.g. picked the fixture up in which case the fixture must not drop itself to the world; it must go into the actor's inventory) - forEachBlockbox { x, y, _, _ -> - if (!BlockCodex[world!!.getTileFromTerrain(x, y)].isActorBlock) { - flagDespawn = true - dropItem = true - } - } - }*/ if (!canBeDespawned) flagDespawn = false // actively deny despawning request if cannot be despawned if (canBeDespawned && flagDespawn) despawn() if (canBeDespawned && dropItem) dropSelfAsAnItem() diff --git a/src/net/torvald/terrarum/modulebasegame/gameitems/ItemSeeds.kt b/src/net/torvald/terrarum/modulebasegame/gameitems/ItemSeeds.kt new file mode 100644 index 000000000..2a4c74e1d --- /dev/null +++ b/src/net/torvald/terrarum/modulebasegame/gameitems/ItemSeeds.kt @@ -0,0 +1,38 @@ +package net.torvald.terrarum.modulebasegame.gameitems + +import com.badlogic.gdx.graphics.g2d.TextureRegion +import net.torvald.terrarum.CommonResourcePool +import net.torvald.terrarum.gameitems.ItemID + +class ItemSeedOak(originalID: ItemID) : FixtureItemBase(originalID, "net.torvald.terrarum.modulebasegame.gameactors.SaplingOak") { + override var originalName = "ITEM_SEED_OAK" + override var baseMass = 10.0 + override val materialId = "OOZE" + override var inventoryCategory = Category.GENERIC + override val itemImage: TextureRegion + get() = CommonResourcePool.getAsItemSheet("basegame.items").get(0,11) +} +class ItemSeedEbony(originalID: ItemID) : FixtureItemBase(originalID, "net.torvald.terrarum.modulebasegame.gameactors.SaplingEbony") { + override var originalName = "ITEM_SEED_EBONY" + override var baseMass = 10.0 + override val materialId = "OOZE" + override var inventoryCategory = Category.GENERIC + override val itemImage: TextureRegion + get() = CommonResourcePool.getAsItemSheet("basegame.items").get(1,11) +} +class ItemSeedBirch(originalID: ItemID) : FixtureItemBase(originalID, "net.torvald.terrarum.modulebasegame.gameactors.SaplingBirch") { + override var originalName = "ITEM_SEED_BIRCH" + override var baseMass = 10.0 + override val materialId = "OOZE" + override var inventoryCategory = Category.GENERIC + override val itemImage: TextureRegion + get() = CommonResourcePool.getAsItemSheet("basegame.items").get(2,11) +} +class ItemSeedRosewood(originalID: ItemID) : FixtureItemBase(originalID, "net.torvald.terrarum.modulebasegame.gameactors.SaplingRosewood") { + override var originalName = "ITEM_SEED_ROSEWOOD" + override var baseMass = 10.0 + override val materialId = "OOZE" + override var inventoryCategory = Category.GENERIC + override val itemImage: TextureRegion + get() = CommonResourcePool.getAsItemSheet("basegame.items").get(3,11) +} \ No newline at end of file diff --git a/src/net/torvald/terrarum/modulebasegame/gameitems/OreItemBase.kt b/src/net/torvald/terrarum/modulebasegame/gameitems/OreItemBase.kt index db9df2342..d4af13385 100644 --- a/src/net/torvald/terrarum/modulebasegame/gameitems/OreItemBase.kt +++ b/src/net/torvald/terrarum/modulebasegame/gameitems/OreItemBase.kt @@ -89,33 +89,6 @@ class ItemLogsRosewood(originalID: ItemID) : OreItemBase(originalID) { -class ItemSeedOak(originalID: ItemID) : OreItemBase(originalID) { - override var originalName = "ITEM_SEED_OAK" - override val materialId = "OOZE" - override val itemImage: TextureRegion - get() = CommonResourcePool.getAsItemSheet("basegame.items").get(0,11) -} -class ItemSeedEbony(originalID: ItemID) : OreItemBase(originalID) { - override var originalName = "ITEM_SEED_EBONY" - override val materialId = "OOZE" - override val itemImage: TextureRegion - get() = CommonResourcePool.getAsItemSheet("basegame.items").get(1,11) -} -class ItemSeedBirch(originalID: ItemID) : OreItemBase(originalID) { - override var originalName = "ITEM_SEED_BIRCH" - override val materialId = "OOZE" - override val itemImage: TextureRegion - get() = CommonResourcePool.getAsItemSheet("basegame.items").get(2,11) -} -class ItemSeedRosewood(originalID: ItemID) : OreItemBase(originalID) { - override var originalName = "ITEM_SEED_ROSEWOOD" - override val materialId = "OOZE" - override val itemImage: TextureRegion - get() = CommonResourcePool.getAsItemSheet("basegame.items").get(3,11) -} - - - class OreStick(originalID: ItemID) : OreItemBase(originalID) { override var originalName = "ITEM_WOOD_STICK" override val materialId = "WOOD" diff --git a/src/net/torvald/terrarum/modulebasegame/worldgenerator/Treegen.kt b/src/net/torvald/terrarum/modulebasegame/worldgenerator/Treegen.kt index c81a0bf36..e3074e85d 100644 --- a/src/net/torvald/terrarum/modulebasegame/worldgenerator/Treegen.kt +++ b/src/net/torvald/terrarum/modulebasegame/worldgenerator/Treegen.kt @@ -1,10 +1,8 @@ package net.torvald.terrarum.modulebasegame.worldgenerator import com.sudoplay.joise.Joise -import net.torvald.random.HQRNG import net.torvald.random.XXHash32 import net.torvald.terrarum.* -import net.torvald.terrarum.App.printdbg import net.torvald.terrarum.blockproperties.Block import net.torvald.terrarum.gameitems.ItemID import net.torvald.terrarum.gameworld.BlockAddress @@ -88,13 +86,7 @@ class Treegen(world: GameWorld, isFinal: Boolean, seed: Long, val terragenParams val thre = this - ibase return if (nextDouble(x, y, h) < 1.0 - thre) ibase else ibase + 1 } - - private fun nextDouble(x: Int, y: Int, h: Int): Double { - return ((XXHash32.hashGeoCoord(x, y) * 31 + h) and 0xFFFFFF) / 16777216.0 - } - private fun nextFloat(x: Int, y: Int, h: Int): Float { - return ((XXHash32.hashGeoCoord(x, y) * 31 + h) and 0xFFFFFF) / 16777216f - } + private fun tryToPlant(xs: IntProgression, ys: Int, grassMap: Array>) { val treeSpecies = 0 @@ -147,12 +139,12 @@ class Treegen(world: GameWorld, isFinal: Boolean, seed: Long, val terragenParams // if there is no grass, grassMap[x] is an empty list if (treeToSpawn[0] != 0) { grassMap[plot1].let { if (it.isEmpty()) null else it.takeRand(xs.first + plot1, ys, 1234) }?.let { - plantTree(xs.first + plot1, it, treeSpecies, 1) // TODO use treeSize from the treeToSpawn + plantTree(world, xs.first + plot1, it, treeSpecies, 1) // TODO use treeSize from the treeToSpawn } } if (treeToSpawn[1] != 0) { grassMap[plot2].let { if (it.isEmpty()) null else it.takeRand(xs.first + plot2, ys, 2345) }?.let { - plantTree(xs.first + plot2, it, treeSpecies, 1) // TODO use treeSize from the treeToSpawn + plantTree(world, xs.first + plot2, it, treeSpecies, 1) // TODO use treeSize from the treeToSpawn } } } @@ -163,7 +155,7 @@ class Treegen(world: GameWorld, isFinal: Boolean, seed: Long, val terragenParams if (treeToSpawn[0] != 0) { val treeSize = arrayOf(null, 0, 1, 2)[treeToSpawn[0]] grassMap[plot1].let { if (it.isEmpty()) null else it.takeRand(xs.first + plot1, ys, 4567) }?.let { - plantTree(xs.first + plot1, it, treeSpecies, treeSize!!) + plantTree(world, xs.first + plot1, it, treeSpecies, treeSize!!) } } } @@ -195,121 +187,126 @@ class Treegen(world: GameWorld, isFinal: Boolean, seed: Long, val terragenParams } // don't use POI -- generate tiles using code for randomisation - /** - * @param y where the grass/dirt tile is - */ - private fun plantTree(x: Int, y: Int, type: Int, size: Int) { - val trunk = "basegame:" + ((if (size <= 1) 64 else 72) + type) - val foliage = "basegame:" + (112 + type) + companion object { + /** + * If a tree cannot be planted on the specified space, `false` will be returned + * + * @param y where the grass/dirt tile is + * @param species 0: oak, 1: ebony, 2: birch, 3: rose + * @param size 0: shrub, 1: normal tree, 2: large tree + */ + fun plantTree(world: GameWorld, x: Int, y: Int, species: Int, size: Int): Boolean { + val trunk = "basegame:" + ((if (size <= 1) 64 else 72) + species) + val foliage = "basegame:" + (112 + species) - var growCnt = 1 - if (size == 0) { - val heightSum = 3 + var growCnt = 1 + if (size == 0) { + val heightSum = 3 - // check for minimum height - val chkM1 = (2..heightSum).any { BlockCodex[world.getTileFromTerrain(x, y - it)].isSolid } - val chk0 = (1..heightSum).any { BlockCodex[world.getTileFromTerrain(x, y - it)].isSolid } - val chkP1 = (2..heightSum).any { BlockCodex[world.getTileFromTerrain(x, y - it)].isSolid } + // check for minimum height + val chkM1 = (2..heightSum).any { BlockCodex[world.getTileFromTerrain(x, y - it)].isSolid } + val chk0 = (1..heightSum).any { BlockCodex[world.getTileFromTerrain(x, y - it)].isSolid } + val chkP1 = (2..heightSum).any { BlockCodex[world.getTileFromTerrain(x, y - it)].isSolid } - if (chkM1 || chk0 || chkP1) { + if (chkM1 || chk0 || chkP1) { // printdbg(this, "Ceiling not tall enough at $x, $y, aborting") - return - } - - val stem = 1 - val bulb1 = 3 + fudgeN(x, y, 4095823) - - // trunk - for (i in 0 until stem) { - for (xi in -1..+1) { - if (xi != 0) { - val tileHere = world.getTileFromTerrain(x + xi, y - growCnt) - if (BlockCodex[tileHere].hasTag("TREETRUNK")) - world.setTileTerrain(x + xi, y - growCnt, Block.AIR, true) - } - else { - world.setTileTerrain(x + xi, y - growCnt, trunk, true) - } + return false } - growCnt += 1 - } - // bulb 1 - growCnt = drawBulb(x, y, 3, bulb1, foliage, growCnt) - } - else if (size == 1) { - val heightSum = 5+3+2+1 - // check for minimum height - val chkM1 = (2..heightSum).any { BlockCodex[world.getTileFromTerrain(x, y - it)].isSolid } - val chk0 = (1..heightSum).any { BlockCodex[world.getTileFromTerrain(x, y - it)].isSolid } - val chkP1 = (2..heightSum).any { BlockCodex[world.getTileFromTerrain(x, y - it)].isSolid } - if (chkM1 || chk0 || chkP1) { + val stem = 1 + val bulb1 = 3 + fudgeN(x, y, 4095823) + + // trunk + for (i in 0 until stem) { + for (xi in -1..+1) { + if (xi != 0) { + val tileHere = world.getTileFromTerrain(x + xi, y - growCnt) + if (BlockCodex[tileHere].hasTag("TREETRUNK")) + world.setTileTerrain(x + xi, y - growCnt, Block.AIR, true) + } + else { + world.setTileTerrain(x + xi, y - growCnt, trunk, true) + } + } + growCnt += 1 + } + // bulb 1 + growCnt = drawBulb(world, x, y, 3, bulb1, foliage, growCnt) + } + else if (size == 1) { + val heightSum = 5 + 3 + 2 + 1 + // check for minimum height + val chkM1 = (2..heightSum).any { BlockCodex[world.getTileFromTerrain(x, y - it)].isSolid } + val chk0 = (1..heightSum).any { BlockCodex[world.getTileFromTerrain(x, y - it)].isSolid } + val chkP1 = (2..heightSum).any { BlockCodex[world.getTileFromTerrain(x, y - it)].isSolid } + + if (chkM1 || chk0 || chkP1) { // printdbg(this, "Ceiling not tall enough at $x, $y, aborting") - return - } + return false + } - // roll for dice until we get a height that fits into the given terrain - val stem = 7 + fudgeN(x, y, 7548291, 1530948) - val bulb1 = 4 + fudgeN(x, y, 345098) - val bulb2 = 3 + fudgeN(x, y, 6093481) - val bulb3 = 2 + fudgeN(x, y, 5413879) + // roll for dice until we get a height that fits into the given terrain + val stem = 7 + fudgeN(x, y, 7548291, 1530948) + val bulb1 = 4 + fudgeN(x, y, 345098) + val bulb2 = 3 + fudgeN(x, y, 6093481) + val bulb3 = 2 + fudgeN(x, y, 5413879) // printdbg(this, "Planting tree at $x, $y; params: $stem, $bulb1, $bulb2, $bulb3") - // trunk - for (i in 0 until stem) { - for (xi in -1..+1) { - if (xi != 0) { - val tileHere = world.getTileFromTerrain(x + xi, y - growCnt) - if (BlockCodex[tileHere].hasTag("TREETRUNK")) - world.setTileTerrain(x + xi, y - growCnt, Block.AIR, true) - } - else { - world.setTileTerrain(x + xi, y - growCnt, trunk, true) + // trunk + for (i in 0 until stem) { + for (xi in -1..+1) { + if (xi != 0) { + val tileHere = world.getTileFromTerrain(x + xi, y - growCnt) + if (BlockCodex[tileHere].hasTag("TREETRUNK")) + world.setTileTerrain(x + xi, y - growCnt, Block.AIR, true) + } + else { + world.setTileTerrain(x + xi, y - growCnt, trunk, true) + } } + growCnt += 1 + } + // bulb base + for (x in x - 2..x + 2) { + val tileHere = world.getTileFromTerrain(x, y - growCnt) + if (BlockCodex[tileHere].hasTag("INCONSEQUENTIAL")) + world.setTileTerrain(x, y - growCnt, foliage, true) } growCnt += 1 + // bulb 1 + growCnt = drawBulb(world, x, y, 7, bulb1, foliage, growCnt) + // bulb 2 + growCnt = drawBulb(world, x, y, 5, bulb2, foliage, growCnt) + // bulb 3 + growCnt = drawBulb(world, x, y, 3, bulb3, foliage, growCnt) } - // bulb base - for (x in x-2..x+2) { - val tileHere = world.getTileFromTerrain(x, y - growCnt) - if (BlockCodex[tileHere].hasTag("INCONSEQUENTIAL")) - world.setTileTerrain(x, y - growCnt, foliage, true) - } - growCnt += 1 - // bulb 1 - growCnt = drawBulb(x, y, 7, bulb1, foliage, growCnt) - // bulb 2 - growCnt = drawBulb(x, y, 5, bulb2, foliage, growCnt) - // bulb 3 - growCnt = drawBulb(x, y, 3, bulb3, foliage, growCnt) - } - else if (size == 2) { - val heightSum = 12+4+3+2+1 - // check for minimum height - val chkM1 = (2..heightSum).any { BlockCodex[world.getTileFromTerrain(x, y - it)].isSolid } - val chk0 = (1..heightSum).any { BlockCodex[world.getTileFromTerrain(x, y - it)].isSolid } - val chkP1 = (2..heightSum).any { BlockCodex[world.getTileFromTerrain(x, y - it)].isSolid } - val chkP2 = (2..heightSum).any { BlockCodex[world.getTileFromTerrain(x, y - it)].isSolid } + else if (size == 2) { + val heightSum = 12 + 4 + 3 + 2 + 1 + // check for minimum height + val chkM1 = (2..heightSum).any { BlockCodex[world.getTileFromTerrain(x, y - it)].isSolid } + val chk0 = (1..heightSum).any { BlockCodex[world.getTileFromTerrain(x, y - it)].isSolid } + val chkP1 = (2..heightSum).any { BlockCodex[world.getTileFromTerrain(x, y - it)].isSolid } + val chkP2 = (2..heightSum).any { BlockCodex[world.getTileFromTerrain(x, y - it)].isSolid } - if (chkM1 || chk0 || chkP1 || chkP2) { + if (chkM1 || chk0 || chkP1 || chkP2) { // printdbg(this, "Ceiling not tall enough at $x, $y, aborting") - return - } + return false + } - // roll for dice until we get a height that fits into the given terrain - val stem = 15 + fudgeN(x, y, 14509, 509348, 412098357) - val bulb1 = 5 + fudgeN(x, y, 1254) - val bulb2 = 4 + fudgeN(x, y, 98134) - val bulb3 = 3 + fudgeN(x, y, 123098) - val bulb4 = 2 + fudgeN(x, y, 8712) + // roll for dice until we get a height that fits into the given terrain + val stem = 15 + fudgeN(x, y, 14509, 509348, 412098357) + val bulb1 = 5 + fudgeN(x, y, 1254) + val bulb2 = 4 + fudgeN(x, y, 98134) + val bulb3 = 3 + fudgeN(x, y, 123098) + val bulb4 = 2 + fudgeN(x, y, 8712) // printdbg(this, "Planting tree at $x, $y; params: $stem, $bulb1, $bulb2, $bulb3, $bulb4") - // soiling - val tl1 = world.getTileFromTerrain(x - 1, y) - val tl2 = world.getTileFromTerrain(x + 1, y) - if (BlockCodex[tl2].hasTag("INCONSEQUENTIAL")) { - world.setTileTerrain(x + 1, y, Block.GRASS, true) - /* + // soiling + val tl1 = world.getTileFromTerrain(x - 1, y) + val tl2 = world.getTileFromTerrain(x + 1, y) + if (BlockCodex[tl2].hasTag("INCONSEQUENTIAL")) { + world.setTileTerrain(x + 1, y, Block.GRASS, true) + /* Case 1 WW WW GG. -> GGG @@ -319,82 +316,94 @@ class Treegen(world: GameWorld, isFinal: Boolean, seed: Long, val terragenParams xG. -> xGG xG. xDG */ - if (tl1 == Block.GRASS) { - world.setTileTerrain(x, y + 1, Block.DIRT, true) + if (tl1 == Block.GRASS) { + world.setTileTerrain(x, y + 1, Block.DIRT, true) - if (BlockCodex[world.getTileFromTerrain(x + 1, y + 1)].hasTag("INCONSEQUENTIAL")) - world.setTileTerrain(x + 1, y + 1, Block.DIRT, true) - } - /* + if (BlockCodex[world.getTileFromTerrain(x + 1, y + 1)].hasTag("INCONSEQUENTIAL")) + world.setTileTerrain(x + 1, y + 1, Block.DIRT, true) + } + /* Case 2 WW WW .G. -> .GG xGx xGx */ + } + // trunk + for (i in 0 until stem) { + for (xi in -1..+2) { + if (xi !in 0..1) { + val tileHere = world.getTileFromTerrain(x + xi, y - growCnt) + if (BlockCodex[tileHere].hasTag("TREETRUNK")) + world.setTileTerrain(x + xi, y - growCnt, Block.AIR, true) + } + else { + world.setTileTerrain(x + xi, y - growCnt, trunk, true) + } + } + growCnt += 1 + } + // bulb base + for (x in x - 2..x + 3) { + val tileHere = world.getTileFromTerrain(x, y - growCnt) + if (BlockCodex[tileHere].hasTag("INCONSEQUENTIAL")) + world.setTileTerrain(x, y - growCnt, foliage, true) + } + growCnt += 1 + for (x in x - 3..x + 4) { + val tileHere = world.getTileFromTerrain(x, y - growCnt) + if (BlockCodex[tileHere].hasTag("INCONSEQUENTIAL")) + world.setTileTerrain(x, y - growCnt, foliage, true) + } + growCnt += 1 + // bulb 1 + growCnt = drawBulb(world, x, y, 10, bulb1, foliage, growCnt) + // bulb 2 + growCnt = drawBulb(world, x, y, 8, bulb2, foliage, growCnt) + // bulb 3 + growCnt = drawBulb(world, x, y, 6, bulb3, foliage, growCnt) + // bulb 4 + growCnt = drawBulb(world, x, y, 4, bulb4, foliage, growCnt) } - // trunk - for (i in 0 until stem) { - for (xi in -1..+2) { - if (xi !in 0..1) { - val tileHere = world.getTileFromTerrain(x + xi, y - growCnt) - if (BlockCodex[tileHere].hasTag("TREETRUNK")) - world.setTileTerrain(x + xi, y - growCnt, Block.AIR, true) - } - else { - world.setTileTerrain(x + xi, y - growCnt, trunk, true) - } + else throw IllegalArgumentException("Unknown tree size: $size") + + return true + } + + private fun drawBulb(world: GameWorld, x: Int, y: Int, width: Int, height: Int, foliage: ItemID, growCnt0: Int): Int { + var growCnt = growCnt0 + val xStart = x - width / 2 + (1 - (width % 2)) + val xEnd = xStart + width + var xStart2 = xStart + var xEnd2 = xEnd + + val r = (XXHash32.hashGeoCoord(x, y) * width * height + growCnt).and(0xffffff) / 16777216f + r.let { + if (it < 0.25) xStart2 += 1 + else if (it < 0.5) xEnd2 -= 1 + } + val xs1 = xStart until xEnd + val xs2 = xStart2 until xEnd2 + + for (i in 0 until height) { + for (x in if (i == height - 1 && i > 0) xs2 else xs1) { + val tileHere = world.getTileFromTerrain(x, y - growCnt) + if (BlockCodex[tileHere].hasTag("INCONSEQUENTIAL")) + world.setTileTerrain(x, y - growCnt, foliage, true) } growCnt += 1 } - // bulb base - for (x in x-2..x+3) { - val tileHere = world.getTileFromTerrain(x, y - growCnt) - if (BlockCodex[tileHere].hasTag("INCONSEQUENTIAL")) - world.setTileTerrain(x, y - growCnt, foliage, true) - } - growCnt += 1 - for (x in x-3..x+4) { - val tileHere = world.getTileFromTerrain(x, y - growCnt) - if (BlockCodex[tileHere].hasTag("INCONSEQUENTIAL")) - world.setTileTerrain(x, y - growCnt, foliage, true) - } - growCnt += 1 - // bulb 1 - growCnt = drawBulb(x, y, 10, bulb1, foliage, growCnt) - // bulb 2 - growCnt = drawBulb(x, y, 8, bulb2, foliage, growCnt) - // bulb 3 - growCnt = drawBulb(x, y, 6, bulb3, foliage, growCnt) - // bulb 4 - growCnt = drawBulb(x, y, 4, bulb4, foliage, growCnt) + return growCnt } - else throw IllegalArgumentException("Unknown tree size: $size") - } - private fun drawBulb(x: Int, y: Int, width: Int, height: Int, foliage: ItemID, growCnt0: Int): Int { - var growCnt = growCnt0 - val xStart = x - width / 2 + (1 - (width % 2)) - val xEnd = xStart + width - var xStart2 = xStart - var xEnd2 = xEnd + /** + * @return normally distributed integer, for `maxvar=1`, `[-1, 0, 1]`; for `maxvar=2`, `[-2, -1, 0, 1, 2]`, etc. + */ + private fun fudgeN(x: Int, y: Int, vararg hs: Int) = hs.sumOf { (nextDouble(x, y, it) * 3).toInt() - 1 } - val r = (XXHash32.hashGeoCoord(x, y) * width * height + growCnt).and(0xffffff) / 16777216f - r.let { - if (it < 0.25) xStart2 += 1 - else if (it < 0.5) xEnd2 -= 1 + private fun nextDouble(x: Int, y: Int, h: Int): Double { + return ((XXHash32.hashGeoCoord(x, y) * 31 + h) and 0xFFFFFF) / 16777216.0 } - val xs1 = xStart until xEnd - val xs2 = xStart2 until xEnd2 - - for (i in 0 until height) { - for (x in if (i == height - 1 && i > 0) xs2 else xs1) { - val tileHere = world.getTileFromTerrain(x, y - growCnt) - if (BlockCodex[tileHere].hasTag("INCONSEQUENTIAL")) - world.setTileTerrain(x, y - growCnt, foliage, true) - } - growCnt += 1 - } - return growCnt } /** @@ -418,11 +427,7 @@ class Treegen(world: GameWorld, isFinal: Boolean, seed: Long, val terragenParams return r } - /** - * @return normally distributed integer, for `maxvar=1`, `[-1, 0, 1]`; for `maxvar=2`, `[-2, -1, 0, 1, 2]`, etc. - */ - private fun fudgeN(x: Int, y: Int, vararg hs: Int) = hs.sumOf { (nextDouble(x, y, it) * 3).toInt() - 1 } - + } data class TreegenParams( diff --git a/work_files/graphics/sprites/saplings.kra b/work_files/graphics/sprites/saplings.kra new file mode 100644 index 000000000..7defafe14 --- /dev/null +++ b/work_files/graphics/sprites/saplings.kra @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ebd3395287876fb45c42de2f9aaa057bb207f959e4d528a7f9146e71ffd4b5a6 +size 97292