diff --git a/assets/mods/basegame/sprites/fixtures/door_basegame-48.tga b/assets/mods/basegame/sprites/fixtures/door_basegame-48.tga new file mode 100644 index 000000000..a9e830c59 --- /dev/null +++ b/assets/mods/basegame/sprites/fixtures/door_basegame-48.tga @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:83cba40a27f72b3741e4447099049c1be40b404e2ce6d6744b0ab7d6438b83d5 +size 27666 diff --git a/assets/mods/basegame/sprites/fixtures/door_basegame-49.tga b/assets/mods/basegame/sprites/fixtures/door_basegame-49.tga new file mode 100644 index 000000000..f45430d65 --- /dev/null +++ b/assets/mods/basegame/sprites/fixtures/door_basegame-49.tga @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:4d5070cd46c9390dc140c3f50282ffe3cad9639023de48e15395704010f9b610 +size 27666 diff --git a/assets/mods/basegame/sprites/fixtures/door_basegame-50.tga b/assets/mods/basegame/sprites/fixtures/door_basegame-50.tga new file mode 100644 index 000000000..8ee1c5819 --- /dev/null +++ b/assets/mods/basegame/sprites/fixtures/door_basegame-50.tga @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:53563ab9be85b82a67ad72beea21ffd0b9f914e653bac714a6d03e9180304ffa +size 27666 diff --git a/assets/mods/basegame/sprites/fixtures/door_basegame-51.tga b/assets/mods/basegame/sprites/fixtures/door_basegame-51.tga new file mode 100644 index 000000000..a6eedaee8 --- /dev/null +++ b/assets/mods/basegame/sprites/fixtures/door_basegame-51.tga @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:322c1662b67467c3c43c76a121bc4ee56e1d91a4bffdd5ebe5f3a6da1740030f +size 27666 diff --git a/src/net/torvald/terrarum/blockproperties/BlockProp.kt b/src/net/torvald/terrarum/blockproperties/BlockProp.kt index 39e3ac0a4..18c09b26d 100644 --- a/src/net/torvald/terrarum/blockproperties/BlockProp.kt +++ b/src/net/torvald/terrarum/blockproperties/BlockProp.kt @@ -106,4 +106,7 @@ class BlockProp { * Mainly intended to be used by third-party modules */ val extra = Codex() + + val isActorBlock: Boolean + get() = nameKey.contains("ACTORBLOCK") } \ No newline at end of file diff --git a/src/net/torvald/terrarum/gameworld/GameWorld.kt b/src/net/torvald/terrarum/gameworld/GameWorld.kt index 96b8a84a1..4fd9612b6 100644 --- a/src/net/torvald/terrarum/gameworld/GameWorld.kt +++ b/src/net/torvald/terrarum/gameworld/GameWorld.kt @@ -269,7 +269,7 @@ open class GameWorld() : Disposable { layerWall.unsafeSetTile(x, y, tilenum) wallDamages.remove(LandUtil.getBlockAddr(this, x, y)) - if (!bypassEvent) { + if (!bypassEvent && oldWall != itemID) { Terrarum.ingame?.queueWallChangedEvent(oldWall, itemID, 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 - if (!bypassEvent) { + if (!bypassEvent && oldTerrain != itemID) { Terrarum.ingame?.queueTerrainChangedEvent(oldTerrain, itemID, x, y) Terrarum.ingame?.modified(LandUtil.LAYER_TERR, x, y) } diff --git a/src/net/torvald/terrarum/gameworld/WorldSimulator.kt b/src/net/torvald/terrarum/gameworld/WorldSimulator.kt index 84161ef90..a80174330 100644 --- a/src/net/torvald/terrarum/gameworld/WorldSimulator.kt +++ b/src/net/torvald/terrarum/gameworld/WorldSimulator.kt @@ -241,7 +241,7 @@ object WorldSimulator { val currentTile = world.getTileFromTerrain(x, y) val prop = BlockCodex[currentTile] // 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 isFallable = support != -1 diff --git a/src/net/torvald/terrarum/modulebasegame/TerrarumIngame.kt b/src/net/torvald/terrarum/modulebasegame/TerrarumIngame.kt index 1ff651b57..c2ad6a6aa 100644 --- a/src/net/torvald/terrarum/modulebasegame/TerrarumIngame.kt +++ b/src/net/torvald/terrarum/modulebasegame/TerrarumIngame.kt @@ -1222,7 +1222,7 @@ open class TerrarumIngame(batch: FlippingSpriteBatch) : IngameInstance(batch) { 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 { @@ -1249,18 +1249,19 @@ open class TerrarumIngame(batch: FlippingSpriteBatch) : IngameInstance(batch) { actorsUnderMouse.forEach { if (it is FixtureBase && it.mainUI == null) fixturesUnderHand.add(it) - else if (it is ActorWithBody) + else mobsUnderHand.add(it) } // 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 fixtureItem = ItemCodex.fixtureToItemID(fixture) printdbg(this, "Fixture pickup at F${WORLD_UPDATE_TIMER}: ${fixture.javaClass.canonicalName} -> $fixtureItem") // 1. put the fixture to the inventory 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) { actor.inventory.add(fixtureItem) actor.equipItem(fixtureItem) diff --git a/src/net/torvald/terrarum/modulebasegame/gameactors/FixtureBase.kt b/src/net/torvald/terrarum/modulebasegame/gameactors/FixtureBase.kt index 73521bc7b..6a31e4869 100644 --- a/src/net/torvald/terrarum/modulebasegame/gameactors/FixtureBase.kt +++ b/src/net/torvald/terrarum/modulebasegame/gameactors/FixtureBase.kt @@ -30,6 +30,10 @@ interface Electric { */ 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 fun blockBoxIndexToPoint2i(it: BlockBoxIndex): Point2i = this.blockBox.width.let { w -> Point2i(it % w, it / w) } var blockBoxProps: BlockBoxProps = BlockBoxProps(0) @@ -72,22 +76,27 @@ open class FixtureBase : ActorWithBody, CuedByTerrainChange { var worldBlockPos: Point2i? = null 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) -> for (y in posY until posY + blockBox.height) { for (x in posX until posX + blockBox.width) { - action(x, y) + action(x, y, x - posX, y - posY) } } } } override fun updateForTerrainChange(cue: IngameInstance.BlockChangeQueueItem) { - fillFillerBlock() + placeActorBlocks() } - private fun fillFillerBlock() { - forEachBlockbox { x, y -> + // something like TapestryObject will want to redefine this + open protected fun placeActorBlocks() { + forEachBlockbox { x, y, _, _ -> //printdbg(this, "fillerblock ${blockBox.collisionType} at ($x, $y)") if (blockBox.collisionType == BlockBox.ALLOW_MOVE_DOWN) { // 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) { - 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 } - 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 worldBlockPos = Point2i(posX, posY) // fill the area with the filler blocks - fillFillerBlock() + placeActorBlocks() this.isVisible = true @@ -160,12 +169,14 @@ open class FixtureBase : ActorWithBody, CuedByTerrainChange { // actually add this actor into the world INGAME.queueActorAddition(this) + spawnRequestedTime = System.nanoTime() return true } + /** force disable despawn when inventory is not empty */ val canBeDespawned: Boolean get() = inventory?.isEmpty() ?: true /** @@ -178,7 +189,7 @@ open class FixtureBase : ActorWithBody, CuedByTerrainChange { printStackTrace(this) // remove filler block - forEachBlockbox { x, y -> + forEachBlockbox { x, y, _, _ -> 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)) } - private var dropItem = false + protected var dropItem = false 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 // 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 (world!!.getTileFromTerrain(x, y) != blockBox.collisionType) { + 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() + // 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) despawn() diff --git a/src/net/torvald/terrarum/modulebasegame/gameactors/FixtureSwingingDoorBase.kt b/src/net/torvald/terrarum/modulebasegame/gameactors/FixtureSwingingDoorBase.kt new file mode 100644 index 000000000..6e6993482 --- /dev/null +++ b/src/net/torvald/terrarum/modulebasegame/gameactors/FixtureSwingingDoorBase.kt @@ -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 = ArrayList() + @Transient override val shadeBoxList: ArrayList = 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) { + + } + } + } + } +} \ No newline at end of file diff --git a/src/net/torvald/terrarum/modulebasegame/gameitems/BlockBase.kt b/src/net/torvald/terrarum/modulebasegame/gameitems/BlockBase.kt index 8d932a50a..db31c6fc0 100644 --- a/src/net/torvald/terrarum/modulebasegame/gameitems/BlockBase.kt +++ b/src/net/torvald/terrarum/modulebasegame/gameitems/BlockBase.kt @@ -8,6 +8,7 @@ import net.torvald.terrarum.gameitems.ItemID import net.torvald.terrarum.gameitems.mouseInInteractableRange import net.torvald.terrarum.gameworld.GameWorld import net.torvald.terrarum.modulebasegame.TerrarumIngame +import net.torvald.terrarum.modulebasegame.gameactors.FixtureBase /** * Created by minjaesong on 2019-05-02. @@ -32,8 +33,8 @@ object BlockBase { if (gameItem.inventoryCategory == GameItem.Category.BLOCK) { var ret1 = true - ingame.actorContainerActive.forEach { - if (it is ActorWithBody && it.physProp.usePhysics && it.intTilewiseHitbox.intersects(mousePoint)) + ingame.actorContainerActive.filter { it is ActorWithBody }.forEach { val it = it as ActorWithBody + if ((it is FixtureBase || it.physProp.usePhysics) && it.intTilewiseHitbox.intersects(mousePoint)) ret1 = false // return is not allowed here } if (!ret1) return@mouseInInteractableRange -1L @@ -44,7 +45,7 @@ object BlockBase { val wallUnderCursor = ingame.world.getTileFromWall(mouseTile.x, mouseTile.y) // 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 // filter passed, do the job diff --git a/src/net/torvald/terrarum/modulebasegame/gameitems/ItemTestDoor.kt b/src/net/torvald/terrarum/modulebasegame/gameitems/ItemTestDoor.kt new file mode 100644 index 000000000..08b0f792f --- /dev/null +++ b/src/net/torvald/terrarum/modulebasegame/gameitems/ItemTestDoor.kt @@ -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() + } + +} \ No newline at end of file diff --git a/work_files/UI/machine_label_font_square.kra b/work_files/UI/machine_label_font_square.kra new file mode 100644 index 000000000..b47ba1899 --- /dev/null +++ b/work_files/UI/machine_label_font_square.kra @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:93acb48e8fa54261fa9f6a92577ae805d17291f4bd08a97f7f37d6971ef59fb1 +size 51705 diff --git a/work_files/graphics/sprites/fixtures/door_wooden.kra b/work_files/graphics/sprites/fixtures/door_wooden.kra new file mode 100644 index 000000000..f80370536 --- /dev/null +++ b/work_files/graphics/sprites/fixtures/door_wooden.kra @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d5678d381349443ade5ffc4dd9a46cead90019a834be741547693cfc879819e6 +size 121812