mirror of
https://github.com/curioustorvald/Terrarum.git
synced 2026-06-15 21:14:04 +09:00
saplings
This commit is contained in:
135
src/net/torvald/terrarum/modulebasegame/gameactors/Cultivable.kt
Normal file
135
src/net/torvald/terrarum/modulebasegame/gameactors/Cultivable.kt
Normal file
@@ -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)
|
||||
@@ -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()
|
||||
|
||||
Reference in New Issue
Block a user