This commit is contained in:
minjaesong
2024-02-03 02:26:58 +09:00
parent d9adbda6f6
commit 93e0c8742a
8 changed files with 391 additions and 221 deletions

View 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)

View File

@@ -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()