diff --git a/ModuleComputers/src/net/torvald/terrarum/modulecomputers/gameitems/ItemHomeComputer.kt b/ModuleComputers/src/net/torvald/terrarum/modulecomputers/gameitems/ItemHomeComputer.kt index f3c870135..c02873eb7 100644 --- a/ModuleComputers/src/net/torvald/terrarum/modulecomputers/gameitems/ItemHomeComputer.kt +++ b/ModuleComputers/src/net/torvald/terrarum/modulecomputers/gameitems/ItemHomeComputer.kt @@ -8,6 +8,7 @@ import net.torvald.terrarum.gameitems.GameItem import net.torvald.terrarum.gameitems.ItemID import net.torvald.terrarum.gameitems.mouseInInteractableRange import net.torvald.terrarum.itemproperties.Material +import net.torvald.terrarum.modulebasegame.gameactors.IngamePlayer import net.torvald.terrarum.modulebasegame.gameitems.FixtureItemBase import net.torvald.terrarum.modulecomputers.gameactors.FixtureHomeComputer @@ -36,7 +37,7 @@ class ItemHomeComputer(originalID: ItemID) : GameItem(originalID) { override fun startPrimaryUse(actor: ActorWithBody, delta: Float) = mouseInInteractableRange(actor) { val item = FixtureHomeComputer() - if (item.spawn(Terrarum.mouseTileX, Terrarum.mouseTileY)) 1L else -1L + if (item.spawn(Terrarum.mouseTileX, Terrarum.mouseTileY, if (actor is IngamePlayer) actor.uuid else null)) 1L else -1L // return true when placed, false when cannot be placed } } \ No newline at end of file diff --git a/src/net/torvald/spriteanimation/SpriteAnimation.kt b/src/net/torvald/spriteanimation/SpriteAnimation.kt index d677ad784..20be67aff 100644 --- a/src/net/torvald/spriteanimation/SpriteAnimation.kt +++ b/src/net/torvald/spriteanimation/SpriteAnimation.kt @@ -51,7 +51,7 @@ class SheetSpriteAnimation(parentActor: ActorWithBody) : SpriteAnimation(parentA /** * Sets delays for each rows. Array size must be the same as the rows of the sheet */ - var delays: FloatArray = floatArrayOf(0.2f) + var delays: FloatArray = FloatArray(64) { 0.2f } set(value) { if (value.filter { it <= 0f }.isNotEmpty()) { throw IllegalArgumentException("Delay array contains zero or negative value: $delays") diff --git a/src/net/torvald/terrarum/modulebasegame/gameactors/FixtureBase.kt b/src/net/torvald/terrarum/modulebasegame/gameactors/FixtureBase.kt index e998724f7..1f1497e40 100644 --- a/src/net/torvald/terrarum/modulebasegame/gameactors/FixtureBase.kt +++ b/src/net/torvald/terrarum/modulebasegame/gameactors/FixtureBase.kt @@ -5,14 +5,13 @@ 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.ActorID -import net.torvald.terrarum.gameactors.ActorWithBody -import net.torvald.terrarum.gameactors.PhysProperties +import net.torvald.terrarum.gameactors.* import net.torvald.terrarum.gameitems.ItemID import net.torvald.terrarum.gameworld.fmod import net.torvald.terrarum.ui.UICanvas import net.torvald.terrarumsansbitmap.gdx.TextureRegionPack import org.dyn4j.geometry.Vector2 +import java.util.* typealias BlockBoxIndex = Int @@ -42,6 +41,8 @@ open class FixtureBase : ActorWithBody, CuedByTerrainChange { @Transient var mainUI: UICanvas? = null var inventory: FixtureInventory? = null + protected var actorThatInstalledThisFixture: UUID? = null + private constructor() : super(RenderOrder.BEHIND, PhysProperties.IMMOBILE, null) @@ -118,7 +119,7 @@ open class FixtureBase : ActorWithBody, CuedByTerrainChange { * @param posY0 tile-wise bottem-centre position of the fixture (usually [Terrarum.mouseTileY]) * @return true if successfully spawned, false if was not (e.g. space to spawn is occupied by something else) */ - open fun spawn(posX0: Int, posY0: Int): Boolean { + open fun spawn(posX0: Int, posY0: Int, installersUUID: UUID?): Boolean { // place filler blocks // place the filler blocks where: // origin posX: centre-left if mouseX is on the right-half of the game window, @@ -169,6 +170,7 @@ open class FixtureBase : ActorWithBody, CuedByTerrainChange { INGAME.queueActorAddition(this) spawnRequestedTime = System.nanoTime() + actorThatInstalledThisFixture = installersUUID return true } @@ -183,7 +185,7 @@ open class FixtureBase : ActorWithBody, CuedByTerrainChange { * @param thbh tile-wise Hitbox height * @return true if successfully spawned, false if was not (e.g. space to spawn is occupied by something else) */ - open fun spawn(posX0: Int, posY0: Int, thbw: Int, thbh: Int): Boolean { + open fun spawn(posX0: Int, posY0: Int, installersUUID: UUID?, thbw: Int, thbh: Int): Boolean { val posX = (posX0 - thbw.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 - thbh + 1 @@ -226,6 +228,7 @@ open class FixtureBase : ActorWithBody, CuedByTerrainChange { INGAME.queueActorAddition(this) spawnRequestedTime = System.nanoTime() + actorThatInstalledThisFixture = installersUUID return true } diff --git a/src/net/torvald/terrarum/modulebasegame/gameactors/FixtureSwingingDoorBase.kt b/src/net/torvald/terrarum/modulebasegame/gameactors/FixtureSwingingDoorBase.kt index 773d76877..818baa4e0 100644 --- a/src/net/torvald/terrarum/modulebasegame/gameactors/FixtureSwingingDoorBase.kt +++ b/src/net/torvald/terrarum/modulebasegame/gameactors/FixtureSwingingDoorBase.kt @@ -6,11 +6,11 @@ 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.terrarum.gameactors.* import net.torvald.terrarumsansbitmap.gdx.TextureRegionPack +import org.dyn4j.geometry.Vector2 +import java.util.* +import kotlin.math.absoluteValue /** * @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) @@ -30,6 +30,8 @@ open class FixtureSwingingDoorBase : FixtureBase, Luminous { open val texturePath = "sprites/fixtures/door_test.tga" open val textureIdentifier = "fixtures-door_test.tga" open val customNameFun = { "DOOR_BASE" } + open val doorClosedHoldLength: Second = 0.1f + open val doorOpenedHoldLength: Second = 0.25f /* END OF CUTOMISABLE PARAMETERS */ private val tilewiseHitboxWidth = tw * 2 - twClosed @@ -42,6 +44,13 @@ open class FixtureSwingingDoorBase : FixtureBase, Luminous { @Transient override val shadeBoxList: ArrayList = ArrayList() protected var doorState = 0 // -1: open toward left, 0: closed, 1: open toward right + protected var doorStateTimer: Second = 0f + + @Transient private val doorHoldLength: HashMap = hashMapOf( + -1 to doorClosedHoldLength, + 1 to doorClosedHoldLength, + 0 to doorOpenedHoldLength + ) // @Transient private var placeActorBlockLatch = false @@ -49,6 +58,7 @@ open class FixtureSwingingDoorBase : FixtureBase, Luminous { BlockBox(BlockBox.FULL_COLLISION, 1, 1), // temporary value, will be overwritten by spawn() nameFun = { "item not loaded properly, alas!" } ) { + nameFun = customNameFun density = 1200.0 @@ -75,7 +85,7 @@ open class FixtureSwingingDoorBase : FixtureBase, Luminous { reload() } - override fun spawn(posX: Int, posY: Int) = spawn(posX, posY, tilewiseHitboxWidth, tilewiseHitboxHeight) + override fun spawn(posX: Int, posY: Int, installersUUID: UUID?): Boolean = spawn(posX, posY, installersUUID, tilewiseHitboxWidth, tilewiseHitboxHeight) override fun reload() { super.reload() @@ -90,18 +100,27 @@ open class FixtureSwingingDoorBase : FixtureBase, Luminous { } open protected fun closeDoor() { - (sprite!! as SheetSpriteAnimation).currentRow = 0 - doorState = 0 + if (doorState != 0) { + (sprite!! as SheetSpriteAnimation).currentRow = 0 + doorState = 0 + placeActorBlocks() + } } open protected fun openToRight() { - (sprite!! as SheetSpriteAnimation).currentRow = 1 - doorState = 1 + if (doorState != 1) { + (sprite!! as SheetSpriteAnimation).currentRow = 1 + doorState = 1 + placeActorBlocks() + } } open protected fun openToLeft() { - (sprite!! as SheetSpriteAnimation).currentRow = 2 - doorState = -1 + if (doorState != -1) { + (sprite!! as SheetSpriteAnimation).currentRow = 2 + doorState = -1 + placeActorBlocks() + } } /*override fun forEachBlockbox(action: (Int, Int, Int, Int) -> Unit) { @@ -142,22 +161,96 @@ open class FixtureSwingingDoorBase : FixtureBase, Luminous { } - override fun update(delta: Float) { - /*if (placeActorBlockLatch) { - placeActorBlockLatch = false - placeActorBlocks() - }*/ + private fun mouseOnLeftSide(): Boolean { + val mouseRelX = Terrarum.mouseX - hitbox.hitboxStart.x + val mouseRelY = Terrarum.mouseY - hitbox.hitboxStart.y + return 0.0 <= mouseRelX && mouseRelX < hitbox.width / 2 && mouseRelY in 0.0..hitbox.height + } + private fun mouseOnRightSide(): Boolean { + val mouseRelX = Terrarum.mouseX - hitbox.hitboxStart.x + val mouseRelY = Terrarum.mouseY - hitbox.hitboxStart.y + return hitbox.width / 2 < mouseRelX && mouseRelX <= hitbox.width && mouseRelY in 0.0..hitbox.height + } + + private fun ActorWithBody.ontheLeftSideOfDoor(): Boolean { + return this.hitbox.centeredX < this@FixtureSwingingDoorBase.hitbox.centeredX + } + private fun ActorWithBody.ontheRightSideOfDoor(): Boolean { + return this.hitbox.centeredX > this@FixtureSwingingDoorBase.hitbox.centeredX + } + + private fun ActorWithBody.movingTowardsRight(): Boolean { + return ((this.controllerV ?: Vector2()) + this.externalV).x >= PHYS_EPSILON_VELO + } + private fun ActorWithBody.movingTowardsLeft(): Boolean { + return ((this.controllerV ?: Vector2()) + this.externalV).x <= -PHYS_EPSILON_VELO + } + private fun ActorWithBody.notMoving(): Boolean { + return ((this.controllerV ?: Vector2()) + this.externalV).x.absoluteValue < PHYS_EPSILON_VELO + } + + override fun update(delta: Float) { super.update(delta) - //if (!flagDespawn) placeActorBlocks() + if (!flagDespawn && worldBlockPos != null) { + val actors = INGAME.actorContainerActive.filterIsInstance() - when (doorState) { - 0/*CLOSED*/ -> { - if (!flagDespawn && worldBlockPos != null) { + // auto opening and closing + // TODO make this work with "player_alies" faction, not just a player + val installer: IngamePlayer? = if (actorThatInstalledThisFixture == null) null else INGAME.actorContainerActive.filterIsInstance().filter { it.uuid == actorThatInstalledThisFixture }.ifEmpty { + INGAME.actorContainerInactive.filterIsInstance().filter { it.uuid == actorThatInstalledThisFixture } + }.getOrNull(0) + // if the door is "owned" by someone, restrict access to its "amicable" (defined using Faction subsystem) actors + // if the door is owned by null, restrict access to ActorHumanoid and actors with "intelligent" actor value set up + if (actorThatInstalledThisFixture == null || installer != null) { + val amicableActors: List = ArrayList( + if (actorThatInstalledThisFixture == null) + actors.filterIsInstance() union actors.filter { it.actorValue.getAsBoolean("intelligent") == true } + else { + val goodFactions = installer?.faction?.flatMap { it.factionAmicable }?.toHashSet() + if (goodFactions != null) + actors.filterIsInstance().filter { + (it.faction.map { it.factionName } intersect goodFactions).isNotEmpty() + } as List + else + listOf() + } + ).also { + // add the installer of the door to the amicableActors if for some reason it was not added + if (installer != null && !it.contains(installer)) it.add(0, installer) + }.filter { + // filter amicableActors so that ones standing near the door remain + it.hitbox.intersects(this.hitbox) + } + + // automatic opening/closing + if (doorStateTimer > doorHoldLength[doorState]!!) { + var nobodyIsThere = true + for (actor in amicableActors) { + if (actor.ontheLeftSideOfDoor() && actor.movingTowardsRight()) { + openToRight() + nobodyIsThere = false + break + } + else if (actor.ontheRightSideOfDoor() && actor.movingTowardsLeft()) { + openToLeft() + nobodyIsThere = false + break + } + } + if (nobodyIsThere) { + closeDoor() + } + + doorStateTimer = 0f } } + + + doorStateTimer += delta } + } } \ No newline at end of file diff --git a/src/net/torvald/terrarum/modulebasegame/gameactors/FixtureTapestry.kt b/src/net/torvald/terrarum/modulebasegame/gameactors/FixtureTapestry.kt index a516a14b8..39e3d52b9 100644 --- a/src/net/torvald/terrarum/modulebasegame/gameactors/FixtureTapestry.kt +++ b/src/net/torvald/terrarum/modulebasegame/gameactors/FixtureTapestry.kt @@ -9,6 +9,7 @@ import net.torvald.terrarum.blockproperties.Block import net.torvald.terrarum.gameitems.ItemID import net.torvald.terrarum.langpack.Lang import net.torvald.terrarumsansbitmap.gdx.TextureRegionPack +import java.util.* import kotlin.properties.Delegates /** @@ -44,7 +45,7 @@ internal class FixtureTapestry : FixtureBase { reload() } - override fun spawn(posX: Int, posY: Int) = spawn(posX, posY, tilewiseHitboxWidth, tilewiseHitboxHeight) + override fun spawn(posX: Int, posY: Int, installersUUID: UUID?): Boolean = spawn(posX, posY, installersUUID, tilewiseHitboxWidth, tilewiseHitboxHeight) override fun reload() { super.reload() diff --git a/src/net/torvald/terrarum/modulebasegame/gameitems/BlockBase.kt b/src/net/torvald/terrarum/modulebasegame/gameitems/BlockBase.kt index db31c6fc0..28484bf78 100644 --- a/src/net/torvald/terrarum/modulebasegame/gameitems/BlockBase.kt +++ b/src/net/torvald/terrarum/modulebasegame/gameitems/BlockBase.kt @@ -44,8 +44,13 @@ object BlockBase { val terrainUnderCursor = ingame.world.getTileFromTerrain(mouseTile.x, mouseTile.y) 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 || BlockCodex[terrainUnderCursor].isActorBlock)) + // return false if there is a same tile already (including non-solid!) + if (isWall && wallUnderCursor == itemID || !isWall && terrainUnderCursor == itemID) + return@mouseInInteractableRange -1L + + // return false if there is a "solid" tile already + 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/FixtureItemBase.kt b/src/net/torvald/terrarum/modulebasegame/gameitems/FixtureItemBase.kt index 3dcdab82a..baeef4455 100644 --- a/src/net/torvald/terrarum/modulebasegame/gameitems/FixtureItemBase.kt +++ b/src/net/torvald/terrarum/modulebasegame/gameitems/FixtureItemBase.kt @@ -10,6 +10,7 @@ import net.torvald.terrarum.gameitems.mouseInInteractableRange import net.torvald.terrarum.itemproperties.Material import net.torvald.terrarum.modulebasegame.TerrarumIngame import net.torvald.terrarum.modulebasegame.gameactors.FixtureBase +import net.torvald.terrarum.modulebasegame.gameactors.IngamePlayer import net.torvald.terrarumsansbitmap.gdx.TextureRegionPack import java.util.concurrent.atomic.AtomicBoolean import java.util.concurrent.atomic.AtomicReference @@ -86,7 +87,7 @@ open class FixtureItemBase(originalID: ItemID, val fixtureClassName: String) : G override fun startPrimaryUse(actor: ActorWithBody, delta: Float) = mouseInInteractableRange(actor) { val item = ghostItem.getAndSet(makeFixture()) // renew the "ghost" otherwise you'll be spawning exactly the same fixture again; old ghost will be returned - if (item.spawn(Terrarum.mouseTileX, Terrarum.mouseTileY)) 1 else -1 + if (item.spawn(Terrarum.mouseTileX, Terrarum.mouseTileY, if (actor is IngamePlayer) actor.uuid else null)) 1 else -1 // return true when placed, false when cannot be placed }