fixed a bug where the player would immediately pick back up the fixture they've just placed one frame ago

This commit is contained in:
minjaesong
2022-07-19 00:25:13 +09:00
parent 23c2d86c27
commit 5fbbf34c10
14 changed files with 260 additions and 23 deletions

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -106,4 +106,7 @@ class BlockProp {
* Mainly intended to be used by third-party modules * Mainly intended to be used by third-party modules
*/ */
val extra = Codex() val extra = Codex()
val isActorBlock: Boolean
get() = nameKey.contains("ACTORBLOCK")
} }

View File

@@ -269,7 +269,7 @@ open class GameWorld() : Disposable {
layerWall.unsafeSetTile(x, y, tilenum) layerWall.unsafeSetTile(x, y, tilenum)
wallDamages.remove(LandUtil.getBlockAddr(this, x, y)) wallDamages.remove(LandUtil.getBlockAddr(this, x, y))
if (!bypassEvent) { if (!bypassEvent && oldWall != itemID) {
Terrarum.ingame?.queueWallChangedEvent(oldWall, itemID, x, y) Terrarum.ingame?.queueWallChangedEvent(oldWall, itemID, x, y)
Terrarum.ingame?.modified(LandUtil.LAYER_WALL, x, y) Terrarum.ingame?.modified(LandUtil.LAYER_WALL, x, y)
} }
@@ -301,7 +301,7 @@ open class GameWorld() : Disposable {
} }
// fluid tiles-item should be modified so that they will also place fluid onto their respective map // fluid tiles-item should be modified so that they will also place fluid onto their respective map
if (!bypassEvent) { if (!bypassEvent && oldTerrain != itemID) {
Terrarum.ingame?.queueTerrainChangedEvent(oldTerrain, itemID, x, y) Terrarum.ingame?.queueTerrainChangedEvent(oldTerrain, itemID, x, y)
Terrarum.ingame?.modified(LandUtil.LAYER_TERR, x, y) Terrarum.ingame?.modified(LandUtil.LAYER_TERR, x, y)
} }

View File

@@ -241,7 +241,7 @@ object WorldSimulator {
val currentTile = world.getTileFromTerrain(x, y) val currentTile = world.getTileFromTerrain(x, y)
val prop = BlockCodex[currentTile] val prop = BlockCodex[currentTile]
// don't let the falling sand destroy the precious storage chest // don't let the falling sand destroy the precious storage chest
val isAir = !prop.isSolid && !prop.nameKey.contains("ACTORBLOCK") val isAir = !prop.isSolid && !prop.isActorBlock
val support = prop.maxSupport val support = prop.maxSupport
val isFallable = support != -1 val isFallable = support != -1

View File

@@ -1222,7 +1222,7 @@ open class TerrarumIngame(batch: FlippingSpriteBatch) : IngameInstance(batch) {
fun performBarehandAction(actor: ActorWithBody, delta: Float) { fun performBarehandAction(actor: ActorWithBody, delta: Float) {
val canAttackOrDig = actor.scale * actor.baseHitboxH >= actor.actorValue.getAsDouble(AVKey.BAREHAND_MINHEIGHT) ?: 4294967296.0 val canAttackOrDig = actor.scale * actor.baseHitboxH >= (actor.actorValue.getAsDouble(AVKey.BAREHAND_MINHEIGHT) ?: 4294967296.0)
fun getActorsAtVicinity(worldX: Double, worldY: Double, radius: Double): List<ActorWithBody> { fun getActorsAtVicinity(worldX: Double, worldY: Double, radius: Double): List<ActorWithBody> {
@@ -1249,18 +1249,19 @@ open class TerrarumIngame(batch: FlippingSpriteBatch) : IngameInstance(batch) {
actorsUnderMouse.forEach { actorsUnderMouse.forEach {
if (it is FixtureBase && it.mainUI == null) if (it is FixtureBase && it.mainUI == null)
fixturesUnderHand.add(it) fixturesUnderHand.add(it)
else if (it is ActorWithBody) else
mobsUnderHand.add(it) mobsUnderHand.add(it)
} }
// pickup a fixture // pickup a fixture
if (fixturesUnderHand.size > 0 && fixturesUnderHand[0].canBeDespawned) { if (fixturesUnderHand.size > 0 && fixturesUnderHand[0].canBeDespawned &&
System.nanoTime() - fixturesUnderHand[0].spawnRequestedTime > 500000000) { // don't pick up the fixture if it was recently placed (0.5 seconds)
val fixture = fixturesUnderHand[0] val fixture = fixturesUnderHand[0]
val fixtureItem = ItemCodex.fixtureToItemID(fixture) val fixtureItem = ItemCodex.fixtureToItemID(fixture)
printdbg(this, "Fixture pickup at F${WORLD_UPDATE_TIMER}: ${fixture.javaClass.canonicalName} -> $fixtureItem") printdbg(this, "Fixture pickup at F${WORLD_UPDATE_TIMER}: ${fixture.javaClass.canonicalName} -> $fixtureItem")
// 1. put the fixture to the inventory // 1. put the fixture to the inventory
fixture.flagDespawn() fixture.flagDespawn()
// 2. register this item(fixture) to the quickslot // 2. register this item(fixture) to the quickslot so that the player sprite would be actually lifting the fixture
if (actor is Pocketed) { if (actor is Pocketed) {
actor.inventory.add(fixtureItem) actor.inventory.add(fixtureItem)
actor.equipItem(fixtureItem) actor.equipItem(fixtureItem)

View File

@@ -30,6 +30,10 @@ interface Electric {
*/ */
open class FixtureBase : ActorWithBody, CuedByTerrainChange { open class FixtureBase : ActorWithBody, CuedByTerrainChange {
/** Real time, in nanoseconds */
@Transient var spawnRequestedTime: Long = 0L
private set
lateinit var blockBox: BlockBox // something like TapestryObject will want to redefine this lateinit var blockBox: BlockBox // something like TapestryObject will want to redefine this
fun blockBoxIndexToPoint2i(it: BlockBoxIndex): Point2i = this.blockBox.width.let { w -> Point2i(it % w, it / w) } fun blockBoxIndexToPoint2i(it: BlockBoxIndex): Point2i = this.blockBox.width.let { w -> Point2i(it % w, it / w) }
var blockBoxProps: BlockBoxProps = BlockBoxProps(0) var blockBoxProps: BlockBoxProps = BlockBoxProps(0)
@@ -72,22 +76,27 @@ open class FixtureBase : ActorWithBody, CuedByTerrainChange {
var worldBlockPos: Point2i? = null var worldBlockPos: Point2i? = null
private set private set
fun forEachBlockbox(action: (Int, Int) -> Unit) { // something like TapestryObject will want to redefine this
/**
* @param action a function with following arguments: posX, posY, offX, offY
*/
open fun forEachBlockbox(action: (Int, Int, Int, Int) -> Unit) {
worldBlockPos?.let { (posX, posY) -> worldBlockPos?.let { (posX, posY) ->
for (y in posY until posY + blockBox.height) { for (y in posY until posY + blockBox.height) {
for (x in posX until posX + blockBox.width) { for (x in posX until posX + blockBox.width) {
action(x, y) action(x, y, x - posX, y - posY)
} }
} }
} }
} }
override fun updateForTerrainChange(cue: IngameInstance.BlockChangeQueueItem) { override fun updateForTerrainChange(cue: IngameInstance.BlockChangeQueueItem) {
fillFillerBlock() placeActorBlocks()
} }
private fun fillFillerBlock() { // something like TapestryObject will want to redefine this
forEachBlockbox { x, y -> open protected fun placeActorBlocks() {
forEachBlockbox { x, y, _, _ ->
//printdbg(this, "fillerblock ${blockBox.collisionType} at ($x, $y)") //printdbg(this, "fillerblock ${blockBox.collisionType} at ($x, $y)")
if (blockBox.collisionType == BlockBox.ALLOW_MOVE_DOWN) { if (blockBox.collisionType == BlockBox.ALLOW_MOVE_DOWN) {
// if the collision type is allow_move_down, only the top surface tile should be "the platform" // if the collision type is allow_move_down, only the top surface tile should be "the platform"
@@ -137,17 +146,17 @@ open class FixtureBase : ActorWithBody, CuedByTerrainChange {
} }
if (hasCollision) { if (hasCollision) {
printdbg(this, "cannot spawn fixture ${nameFun()}, has tile collision; tilewise dim: (${blockBox.width}, ${blockBox.height}) ") printdbg(this, "cannot spawn fixture ${nameFun()} at F${INGAME.WORLD_UPDATE_TIMER}, has tile collision; tilewise dim: (${blockBox.width}, ${blockBox.height}) ")
return false return false
} }
printdbg(this, "spawn fixture ${nameFun()}, tilewise dim: (${blockBox.width}, ${blockBox.height})") printdbg(this, "spawn fixture ${nameFun()} at F${INGAME.WORLD_UPDATE_TIMER}, tilewise dim: (${blockBox.width}, ${blockBox.height})")
// set the position of this actor // set the position of this actor
worldBlockPos = Point2i(posX, posY) worldBlockPos = Point2i(posX, posY)
// fill the area with the filler blocks // fill the area with the filler blocks
fillFillerBlock() placeActorBlocks()
this.isVisible = true this.isVisible = true
@@ -160,12 +169,14 @@ open class FixtureBase : ActorWithBody, CuedByTerrainChange {
// actually add this actor into the world // actually add this actor into the world
INGAME.queueActorAddition(this) INGAME.queueActorAddition(this)
spawnRequestedTime = System.nanoTime()
return true return true
} }
/** force disable despawn when inventory is not empty */
val canBeDespawned: Boolean get() = inventory?.isEmpty() ?: true val canBeDespawned: Boolean get() = inventory?.isEmpty() ?: true
/** /**
@@ -178,7 +189,7 @@ open class FixtureBase : ActorWithBody, CuedByTerrainChange {
printStackTrace(this) printStackTrace(this)
// remove filler block // remove filler block
forEachBlockbox { x, y -> forEachBlockbox { x, y, _, _ ->
world!!.setTileTerrain(x, y, Block.AIR, true) world!!.setTileTerrain(x, y, Block.AIR, true)
} }
@@ -204,19 +215,36 @@ open class FixtureBase : ActorWithBody, CuedByTerrainChange {
INGAME.queueActorAddition(DroppedItem(drop, hitbox.startX, hitbox.startY - 1.0)) INGAME.queueActorAddition(DroppedItem(drop, hitbox.startX, hitbox.startY - 1.0))
} }
private var dropItem = false protected var dropItem = false
override fun update(delta: Float) { override fun update(delta: Float) {
if (!flagDespawn && worldBlockPos != null) { // 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 // removal-by-player because player is removing the filler block by pick
// no-flagDespawn check is there to prevent item dropping when externally despawned // 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) // (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 -> forEachBlockbox { x, y, _, _ ->
if (world!!.getTileFromTerrain(x, y) != blockBox.collisionType) { if (!BlockCodex[world!!.getTileFromTerrain(x, y)].isActorBlock) {
flagDespawn = true flagDespawn = true
dropItem = true dropItem = true
} }
} }
}*/
if (!canBeDespawned) flagDespawn = false // actively deny despawning request if cannot be despawned
if (canBeDespawned && flagDespawn) despawn()
if (canBeDespawned && dropItem) dropSelfAsAnItem()
// actual actor removal is performed by the TerrarumIngame.killOrKnockdownActors
super.update(delta)
}
/**
* An alternative to `super.update()`
*/
fun updateWithCustomActorBlockFun(delta: Float, actorBlockFillingFunction: () -> Unit) {
if (!flagDespawn && worldBlockPos != null) {
actorBlockFillingFunction()
} }
if (!canBeDespawned) flagDespawn = false if (!canBeDespawned) flagDespawn = false
if (canBeDespawned && flagDespawn) despawn() if (canBeDespawned && flagDespawn) despawn()

View File

@@ -0,0 +1,151 @@
package net.torvald.terrarum.modulebasegame.gameactors
import net.torvald.spriteanimation.SheetSpriteAnimation
import net.torvald.terrarum.*
import net.torvald.terrarum.App.printdbg
import net.torvald.terrarum.TerrarumAppConfiguration.TILE_SIZE
import net.torvald.terrarum.TerrarumAppConfiguration.TILE_SIZED
import net.torvald.terrarum.blockproperties.Block
import net.torvald.terrarum.gameactors.AVKey
import net.torvald.terrarum.gameactors.Hitbox
import net.torvald.terrarum.gameactors.Lightbox
import net.torvald.terrarum.gameactors.Luminous
import net.torvald.terrarumsansbitmap.gdx.TextureRegionPack
/**
* @param width of hitbox, in tiles, when the door is opened. Default to 2. Closed door always have width of 1. (this limits how big and thick the door can be)
* @param height of hitbox, in ties. Default to 3.
*
* Created by minjaesong on 2022-07-15.
*/
class FixtureSwingingDoorBase : FixtureBase, Luminous {
/* OVERRIDE THESE TO CUSTOMISE */
open val tw = 2
open val th = 3
open val twClosed = 1
open val opacity = BlockCodex[Block.STONE].opacity
open val isOpacityActuallyLuminosity = false
open val moduleName = "basegame"
open val texturePath = "sprites/fixtures/door_test.tga"
open val textureIdentifier = "fixtures-door_test.tga"
open val customNameFun = { "DOOR_BASE" }
/* END OF CUTOMISABLE PARAMETERS */
@Transient override val lightBoxList: ArrayList<Lightbox> = ArrayList()
@Transient override val shadeBoxList: ArrayList<Lightbox> = ArrayList()
protected var doorState = 0 // -1: open toward left, 0: closed, 1: open toward right
@Transient private var placeActorBlockLatch = false
constructor() : super(
BlockBox(BlockBox.FULL_COLLISION, 1, 3), // temporary value, will be overwritten by reload()
nameFun = { "item not loaded properly, alas!" }
) {
reload()
placeActorBlockLatch = true
}
override fun reload() {
super.reload()
nameFun = customNameFun
val hbw = TILE_SIZE * (tw * 2 - twClosed)
val hbh = TILE_SIZE * th
blockBox = BlockBox(BlockBox.FULL_COLLISION, tw * 2 - twClosed, th)
// loading textures
CommonResourcePool.addToLoadingList("$moduleName-$textureIdentifier") {
TextureRegionPack(ModMgr.getGdxFile(moduleName, texturePath), hbw, hbh)
}
CommonResourcePool.loadAll()
density = 1200.0
actorValue[AVKey.BASEMASS] = 10.0
// setHitboxDimension(hbw, hbh, TILE_SIZE * (tw * 2 - twClosed), 0)
setHitboxDimension(hbw, hbh, TILE_SIZE * ((tw * 2 - twClosed - 1) / 2), 0)
(if (isOpacityActuallyLuminosity) lightBoxList else shadeBoxList).add(
Lightbox(Hitbox(0.0, 0.0, TILE_SIZED, th * TILE_SIZED), opacity))
makeNewSprite(FixtureBase.getSpritesheet(moduleName, texturePath, hbw, hbh)).let {
it.setRowsAndFrames(3,1)
}
placeActorBlockLatch = false
}
open protected fun closeDoor() {
(sprite!! as SheetSpriteAnimation).currentRow = 0
doorState = 0
}
open protected fun openToRight() {
(sprite!! as SheetSpriteAnimation).currentRow = 1
doorState = 1
}
open protected fun openToLeft() {
(sprite!! as SheetSpriteAnimation).currentRow = 2
doorState = -1
}
override fun forEachBlockbox(action: (Int, Int, Int, Int) -> Unit) {
val xStart = worldBlockPos!!.x - ((tw * 2 - twClosed - 1) / 2) // worldBlockPos.x is where the mouse was, of when the tilewise width was 1.
for (y in worldBlockPos!!.y until worldBlockPos!!.y + blockBox.height) {
for (x in xStart until xStart + blockBox.width) {
action(x, y, x - xStart, y - worldBlockPos!!.y)
}
}
}
override fun placeActorBlocks() {
forEachBlockbox { x, y, ox, oy ->
val tile = when (doorState) {
// CLOSED --
// fill the actorBlock so that:
// N..N F N..N (repeated `th` times; N: no collision, F: full collision)
0/*CLOSED*/ -> {
if (ox in tw-1 until tw-1+twClosed) Block.ACTORBLOCK_FULL_COLLISION
else Block.ACTORBLOCK_NO_COLLISION
}
else/*OPENED*/ -> Block.ACTORBLOCK_NO_COLLISION
}
world!!.setTileTerrain(x, y, tile, false)
}
}
override fun flagDespawn() {
super.flagDespawn()
printdbg(this, "flagged to despawn")
printStackTrace(this)
}
override fun updateForTerrainChange(cue: IngameInstance.BlockChangeQueueItem) {
}
override fun update(delta: Float) {
if (placeActorBlockLatch) {
placeActorBlockLatch = false
placeActorBlocks()
}
super.update(delta)
//if (!flagDespawn) placeActorBlocks()
when (doorState) {
0/*CLOSED*/ -> {
if (!flagDespawn && worldBlockPos != null) {
}
}
}
}
}

View File

@@ -8,6 +8,7 @@ import net.torvald.terrarum.gameitems.ItemID
import net.torvald.terrarum.gameitems.mouseInInteractableRange import net.torvald.terrarum.gameitems.mouseInInteractableRange
import net.torvald.terrarum.gameworld.GameWorld import net.torvald.terrarum.gameworld.GameWorld
import net.torvald.terrarum.modulebasegame.TerrarumIngame import net.torvald.terrarum.modulebasegame.TerrarumIngame
import net.torvald.terrarum.modulebasegame.gameactors.FixtureBase
/** /**
* Created by minjaesong on 2019-05-02. * Created by minjaesong on 2019-05-02.
@@ -32,8 +33,8 @@ object BlockBase {
if (gameItem.inventoryCategory == GameItem.Category.BLOCK) { if (gameItem.inventoryCategory == GameItem.Category.BLOCK) {
var ret1 = true var ret1 = true
ingame.actorContainerActive.forEach { ingame.actorContainerActive.filter { it is ActorWithBody }.forEach { val it = it as ActorWithBody
if (it is ActorWithBody && it.physProp.usePhysics && it.intTilewiseHitbox.intersects(mousePoint)) if ((it is FixtureBase || it.physProp.usePhysics) && it.intTilewiseHitbox.intersects(mousePoint))
ret1 = false // return is not allowed here ret1 = false // return is not allowed here
} }
if (!ret1) return@mouseInInteractableRange -1L if (!ret1) return@mouseInInteractableRange -1L
@@ -44,7 +45,7 @@ object BlockBase {
val wallUnderCursor = ingame.world.getTileFromWall(mouseTile.x, mouseTile.y) val wallUnderCursor = ingame.world.getTileFromWall(mouseTile.x, mouseTile.y)
// return false if there is a tile already // return false if there is a tile already
if (isWall && BlockCodex[wallUnderCursor].isSolid || !isWall && BlockCodex[terrainUnderCursor].isSolid) if (isWall && BlockCodex[wallUnderCursor].isSolid || !isWall && (BlockCodex[terrainUnderCursor].isSolid || BlockCodex[terrainUnderCursor].isActorBlock))
return@mouseInInteractableRange -1L return@mouseInInteractableRange -1L
// filter passed, do the job // filter passed, do the job

View File

@@ -0,0 +1,35 @@
package net.torvald.terrarum.modulebasegame.gameitems
import com.badlogic.gdx.graphics.g2d.TextureRegion
import net.torvald.terrarum.CommonResourcePool
import net.torvald.terrarum.gameitems.ItemID
import net.torvald.terrarum.itemproperties.Material
import net.torvald.terrarum.modulebasegame.gameactors.FixtureBase
import net.torvald.terrarum.modulebasegame.gameactors.FixtureSwingingDoorBase
/**
* Created by minjaesong on 2022-07-15.
*/
class ItemTestDoor(originalID: ItemID) : FixtureItemBase(originalID, "net.torvald.terrarum.modulebasegame.gameactors.FixtureSwingingDoorBase") {
override var dynamicID: ItemID = originalID
override val originalName = "ITEM_DOOR"
override var baseMass = 12.0
override var stackable = true
override var inventoryCategory = Category.MISC
override val isUnique = false
override val isDynamic = false
override val material = Material()
override val itemImage: TextureRegion
get() = CommonResourcePool.getAsTextureRegion("itemplaceholder_16")
override var baseToolSize: Double? = baseMass
init {
equipPosition = EquipPosition.HAND_GRIP
}
override val makeFixture: () -> FixtureBase = {
FixtureSwingingDoorBase()
}
}

Binary file not shown.

Binary file not shown.