1. Please be advised of a few specific rules and guidelines for this section.

RELEASED Automatic Doors 3.9

Makes all doors open and close automatically!

  1. lornlynx

    lornlynx Cosmic Narwhal

    @bk3k : Ok, I think I cleared the bugs with the 3.1 version, the problem was that doors that got opened didn't stay open if you reopened the game, this caused the doors to take the close animation even though they were open. I fixed this in the initialization and I've also updated the version with the latest changes from 2.9, and changed storage.isOpen to storage.state again for obvious reasons. I don't really know if I didn't mess up some other stuff with that changes in the init() as I'm a bit unclear how you want it to work, so rather watch over it.
    You may take it up from here and add your stuff if you like:
    Code:
    -- automatic doors 3.2
    -- basic code by Chucklefish, additional code by lornlynx (with some help from Healthire), greatly reworked and updated by bk3k
    -- the additional code  is documentated
    
    function init()
      message.setHandler("openDoor", function() openDoor() end)
      message.setHandler("lockDoor", function() lockDoor() end)
    
      storage.objectName = config.getParameter("objectName", "door")
      storage.defaultLocked = config.getParameter("defaultlocked", false)
      storage.maxInputNode = ( #(config.getParameter("inputNodes", {})) - 1 )
      storage.defaultInteractive = config.getParameter("interactive", true)
      storage.wireOpened = false
      self.Interacted = false
      self.npcClosed = false
      self.closeCooldown = 0
      self.openCooldown = 0
      storage.wireControlled = object.isInputNodeConnected(0)
      --^^this should fix the mission doors!
      self.noClearOpen = object.isOutputNodeConnected(0)
      --^^another issue fixed
      storage.openingAnimation_stateName = config.getParameter("openingAnimation", "open") 
      --if doors have an opening animation cycle and frames seperate from the "open" state
      --my doors use "opening"
      storage.lockedAnimation_stateName = config.getParameter("lockedAnimation", "closed")
      --if they have an actual locked animation and frames, use instead of "closed" when locked
      --my doors use "locked"
    
      setDirection(storage.doorDirection or object.direction())
      initializeMaterialSpaces()
      
      if (storage.locked == nil) then 
        storage.locked = ( storage.defaultLocked or config.getParameter("locked", false) )
      end
    
      if (storage.state == nil) then
        storage.state = (config.getParameter("defaultState", "closed") == "open")
        realOpenDoor(storage.doorDirection)
      elseif storage.locked then
        lockDoor()
      else
        animator.setAnimationState("doorState", storage.state and "open" or "closed")
      end
      
      --called only in init(), use before updateMaterialSpaces()
    
      -- if storage.state and not storage.locked and not (storage.wireControlled and not storage.wireOpened) then
      --   realOpenDoor(storage.doorDirection)
      -- elseif storage.locked then
      --   lockDoor()
      -- else
      --   animator.setAnimationState("doorState", storage.state and "open" or "closed")
      -- end
    
      updateInteractive()
      --updateCollisionAndWires()
      --updateLight()
      --opening/locking/closing door handles this already
    
      --automatic door specific stuff below
      storage.doorException = doorException() --check if door is part of exception list and return true or false
      noAutomatic()  --will set storage.noAuto if door should not be automatic
    
      if storage.noAuto then
        return
      end  --no need for the rest, save a few CPU cycles
    
      storage.boundVar = config.getParameter("detectBoundMode", "CollisionArea")
      setIncludedTypes()
      storage.isHorizontal = isDoorHorizontal() 
      storage.playerOpened = storage.playerClosed or false
      storage.playerClosed = storage.playerClosed or false
      setQuery()
    end
    
    
    function setIncludedTypes()
      storage.scanTargets = config.getParameter("scanTargets", {"player", "vehicle"})
      if not (#storage.scanTargets > 0) or not (type(storage.scanTargets) == "table") then
        storage.scatTargets = {"player", "vehicle"}
      end
    end
    
    
    function setQuery()  --new function of code moved from update() and called by init() so only done once
    
      --local objectName = config.getParameter("objectName", "door")
      storage.doorPosition = object.position()
      storage.queryRadius = config.getParameter("queryOpenRadius", 5) --I don't like the door opening directly in my face
    
      if storage.queryRadius == nil then
        storage.queryRadius = 0
      end
    
      if (storage.objectName == "lunarbasedoor") then
        storage.queryRadius = 5.5
        storage.doorPosition[2] = storage.doorPosition[2] + 2.5
        storage.doorPosition[1] = storage.doorPosition[1]
        -- world.debugLine({storage.doorPosition[1], storage.doorPosition[2]}, {storage.doorPosition[1] + storage.queryRadius, storage.doorPosition[2] + storage.queryRadius}, "blue")
        -- changes door position slightly to make scanning position at height of half
        -- the door (or width if horizontal door)
      elseif storage.isHorizontal then
        storage.doorPosition[1] = storage.doorPosition[1] + 2.5
        storage.doorPosition[2] = storage.doorPosition[2]
        -- world.debugLine(getQueryArea(storage.doorPosition, storage.queryRadius)[1], getQueryArea(storage.doorPosition, storage.queryRadius)[2], "blue")
      else
        storage.doorPosition[2] = storage.doorPosition[2] + 2.5
        storage.doorPosition[1] = storage.doorPosition[1] + 0.5
        -- world.debugLine({storage.doorPosition[1], storage.doorPosition[2]}, {storage.doorPosition[1] + storage.queryRadius, storage.doorPosition[2] + storage.queryRadius}, "blue")
      end
    
      -- sb.loginfo("dt")
      -- world.debugPoint(storage.doorPosition, "green")
      -- world.debugPoint(object.position(), "red")
      -- world.debugText("pos: %s, %s", storage.doorPosition[1], storage.doorPosition[2], {object.position()[1], object.position()[2] + 1}, "black")
    
      -- checks for players around and saves
      -- them in array.
      self.queryArea = getQueryArea(object.position(), storage.queryRadius)
    end
    
    
    function onNodeConnectionChange(args)
      updateInteractive()
      storage.wireControlled = object.isInputNodeConnected(0)
      onInputNodeChange({ level = object.getInputNodeLevel(0) })
      updateCollisionAndWires()
      self.noClearOpen = object.isOutputNodeConnected(0)
    end
    
    
    function onInputNodeChange(args)  --modified
    -- @tab args Map of:
    --  {
    --  node = <(int) index of the node that is changing>
    --  level = <new level of the node>
    --  }
      if storage.maxInputNode > 0 then
        --delegate to another function
        onInputMultiNodeChange(args)
        return
      end
      
      if args.level then
        storage.wireOpened = true
        realOpenDoor(storage.doorDirection)
      else
        storage.wireOpened = false
        realCloseDoor()
      end
    end
    
    
    function onInputMultiNodeChange(args) --added
      if storage.defaultLocked then
        --delegate to another function
        secureControl(args)
        return
      end
    
      storage.wireOpened = false
      local n = 0
      while (n <= storage.maxInputNode) do
        if object.getInputNodeLevel(n) then
          storage.wireOpened = true
          break  --no need to continue if found any active wire
        end
        n = n + 1
      end
    
      if storage.wireOpened and not storage.state then
        realOpenDoor(storage.doorDirection)
      elseif storage.state then
        realCloseDoor()
      else
      
      end
    
    end
    
    
    function secureControl ()
      if object.getInputNodeLevel(0) and object.getInputNodeLevel(1) then --this requires multiple inputs to open, else lock
        unlockDoor()
        realOpenDoor(storage.doorDirection)
      else
        lockDoor()
      end
      --I may expand this later to demand wire inputs that conform to a pattern.  Such as having 8 input nodes,
      --and only opening when only the correct nodes are activated at once, perhaps in sequence!
      --For now think of this as a built-in AND switch
    end
    
    
    function onInteraction(args)
      if storage.locked then
        animator.playSound("locked")
      return
      end
    
      self.Interacted = true
      --because storage.state will soon flip value
      storage.playerClosed = storage.state
      storage.playerOpened = not storage.state
    
      if not storage.state then
        if storage.isHorizontal then
          -- give the door a cooldown before closing again
          realOpenDoor(args.source[2])
          self.closeCooldown = 2  --increased cooldown
        else
          realOpenDoor(args.source[1])
          self.closeCooldown = 0
        end
      else
        realCloseDoor()
      end
      
    end
    
    
    function updateLight()
      if not storage.state then
        object.setLightColor(config.getParameter("closedLight", {0,0,0,0}))
      else
        object.setLightColor(config.getParameter("openLight", {0,0,0,0}))
      end
    end
    
    
    function updateInteractive()
      object.setInteractive(storage.defaultInteractive and not (object.isInputNodeConnected(0) or storage.defaultLocked or storage.locked))
    end
    
    
    function updateCollisionAndWires()
      updateMaterialSpaces()
      object.setMaterialSpaces(storage.state and storage.openMaterialSpaces or storage.closedMaterialSpaces)
      object.setAllOutputNodes(storage.state)
    end
    
    
    function updateMaterialSpaces()
      if object.isInputNodeConnected(0) then
        storage.closedMaterialSpaces = storage.materialTable[1]
      else
        storage.closedMaterialSpaces = storage.materialTable[2]
      end
    
    end
    
    
    function initializeMaterialSpaces()
      --forget the vanilla idea of reading attributes and rebuilding tables every time
      --lets just build and store 2 tables at init() time
      --and switch between them as needed with updateMaterialSpaces()
      storage.openMaterialSpaces = config.getParameter("openMaterialSpaces", {})
      storage.closedMaterialSpaces = config.getParameter("closedMaterialSpaces", {})
      storage.materialTable = { {}, {} }
      local metamaterial = {"metamaterial:door", "metamaterial:lockedDoor"}
      local j = 1
      local count = 2 --could use #metamaterial but why bother?
      local allSpaces = object.spaces()
      while j <= count do
        for i, space in ipairs(allSpaces) do
          table.insert(storage.materialTable[j], {space, metamaterial[j]})
        end
        j = j + 1
      end
      updateCollisionAndWires()
    
    end
    
    
    function setDirection(direction)
      storage.doorDirection = direction
      animator.setGlobalTag("doorDirection", direction < 0 and "Left" or "Right")
    end
    
    
    function hasCapability(capability)
      --this is called by
      --scripts/actions/movement.lua
      --scripts/pathing.lua
      --it would be more accurate to call the argument "currentState" but I'll preserve the original name anyhow
      if (capability == 'lockedDoor') then
        return storage.locked
        --elseif (object.isInputNodeConnected(0) or storage.wireOpened or storage.locked or (self.closeCooldown > 0) or (self.openCooldown > 0)) then
      elseif (object.isInputNodeConnected(0) or storage.wireOpened or storage.locked ) then
        return false
      elseif (capability == 'door') then
        return true
      elseif (capability == 'closedDoor') then
        return not storage.state
      elseif (capability == 'openDoor') then
        return storage.state
      else
        return false
      end
    end
    
    
    function doorOccupiesSpace(position)
      --used by objects/spawner/colonydeed/scanner.lua
      local relative = {position[1] - object.position()[1], position[2] - object.position()[2]}
      for _, space in ipairs(object.spaces()) do
        if math.floor(relative[1]) == space[1] and math.floor(relative[2]) == space[2] then
          return true
        end
      end
      return false
    end
    
    
    function lockDoor() --added because oddly because there is no locking function even while an unlocking one
      if not storage.locked and (self.closeCooldown >= 0) then
        if storage.state then
          animator.setAnimationState("doorState", "locking")
          storage.state = false
          animator.playSound("close")
          updateCollisionAndWires()
          updateLight()
        else --no need to close door etc, just change animation state
          animator.setAnimationState("doorState", storage.lockedAnimation_stateName)
          updateCollisionAndWires()
          updateLight()
        end
        storage.locked = true
      end
    end
    
    
    function unlockDoor()
      storage.locked = false
      updateInteractive()
      if not storage.state then
        animator.setAnimationState("doorState", "closed")
      end
    end
    
    
    function realCloseDoor()
      -- only close door when cooldown is zero
      --if storage.state and (self.closeCooldown <= 0) then
      if storage.state then
        storage.state = false
        animator.playSound("close")
        animator.setAnimationState("doorState", "closing")
      end
      updateCollisionAndWires()
      updateLight()
      -- world.debugText("Close!", object.position(), "red")
    end
    
    
    function closeDoor()
      --all internal functions will use realCloseDoor()
      --see openDoor() for why
    if storage.wireControlled then return end
      self.npcClosed = true
      self.openCooldown = 2
      realCloseDoor()
    end
    
    
    function openDoor(direction)
      --all internal functions will use realOpenDoor()
      --therefore if this is called, we know it is externally sourced and can take extra measures
      --which is why I renamed the original function
    
      --movement.lua and pathing.lua use this
      --world.callScriptedEntity(doorId, "openDoor")
      --some NPCs have the intended capacity to open locked doors and this will do just as well
      --the ones that do not will not even attempt to open a locked door
      --and if wired on node 0, hasCapability() will return false to all capacities except locked
      --so that's one area we got in trouble with before is NPCs who expect to open locked doors
    
      --I'm still unsure under what conditions message.setHandler() is used
      --go ahead and delete this block of comments after you've read them!
    
      unlockDoor()
      self.closeCooldown = 2
      if (direction == nil) then
        setDirectionNPC()
      end
      realOpenDoor(direction)
      
      --sb.logInfo("door.lua/openDoor() used on door with id" .. ( tostring( entity.id() ) ) .. "at position ".. ( tostring( entity.position() ) ) )
    end
    
    
    function realOpenDoor(direction)
      if not storage.state then
        storage.state = true
        setDirection((direction == nil or direction * object.direction() < 0) and -1 or 1)
        animator.playSound("open")
        animator.setAnimationState("doorState", storage.openingAnimation_stateName)
      
        if storage.isHorizontal and not self.interacted then
          self.openCooldown = 2
        end
      end
    
      updateCollisionAndWires()
      updateLight()
      -- world.debugText("Open!", object.position(), "red")
    end
    
    
    -- Checks if doors is horizontal, depending on different use of anchors.
    --
    -- @return BOOL value for confirmation
    --
    function isDoorHorizontal()
    --expanded upon to have a backup should there be no anchors, instead testing for itself in either horizontal direction.
    --going to switch to spaces() check 
    --This will only run from init()
      local theAnswer = config.getParameter("horizontal_door")
      if (theAnswer ~= nil) and (type(theAnswer) == "boolean") then
        return theAnswer
      end
      --if this is set, no need to further check
      
      local anchors = config.getParameter("anchors", {"top", "bottom"})
      for _,anchor in ipairs(anchors) do
        if anchor == "left" or anchor == "right" then
          return true
        end
      end
      
      theAnswer = false --lets assume false for now
      local toTheRight = {}
      local toTheLeft = {}
      toTheRight[1] = object.position()[1] + 3
      toTheRight[2] = object.position()[2]
      toTheLeft[1] = object.position()[1] - 3
      toTheLeft[2] = object.position()[2]
      --I suppose that would fail if verticle door was 4 thick, but I've never seen thicker than 3 (and that's a bit excessive already)
    
      local rightTable = world.objectQuery(toTheRight, 0)
      local leftTable = world.objectQuery(toTheLeft, 0)
    
      for k,v in pairs(rightTable) do
        table.insert( leftTable, v )
      end  --merged leftTable into RightTable
      local rtSize = #rightTable
    
      local i = 1
      while (i <= rtSize) do
        if (rightTable[i] == entity.id()) then
          theAnswer = true
          break
        end
        i = i + 1
      end --search all entities returned in this table, and check if the id matches this object.
      
      return theAnswer
    end
    
    
    -- Modifies query values for horizontal doors
    --
    -- If the door is horizontal, the position and radius are used as min & max
    -- positions which causes Query to use a rectangular scanning area.
    --
    -- @tab position Default door position
    -- @tab radius Wanted radius/sidelenght for scanning area
    --
    -- @return minPos Position for left bottom corner of scanning rectangle
    -- @return minPos Position for right top corner of scanning rectangle
    -- @return position Does get return unmodified if vertical door
    -- @return radius Does get return unmodified if vertical door
    --
    function getQueryArea(position, radius)
      if storage.isHorizontal then
        local minPos = {position[1] - radius, position[2] - radius}
        local maxPos = {position[1] + radius, position[2]} -- Don't query above, want players to walk on the door
        return {minPos, maxPos}
      else
        return {position, radius}
      end
    end
    
    
    function noAutomatic() --added
      --sets boolean variable to determine if automatic functionality will be disabled for this door
      storage.noAuto = (storage.doorException or storage.defaultLocked or config.getParameter("noAutomaticDoors", false))
    end
    
    
    function doorException() --added, call this before noAutomatic() in init()
      --I'd really prefer to load this from JSON, but don't know that Starbound would allow access beyond current object
      --so manual table loading it is!  This seems more managable in case more exceptions get added.
      local doorTable = {
      "castlehiddentrapdoor",
      "castlehiddendoor",
      "templehiddentrapdoor",
      "pilch_horizdoor",
      "dirttrapdoor",
      "stonedoor",
      "ancientlightplatform",
      "ancienthiddenplatform",
      "templepressureplatform" }
    
      local doorCount = #doorTable
      local i = 1
      while (i <= doorCount) do
        if (doorTable[i] == storage.objectName) then
          return true
        end
        i = i + 1
      end
      return false
    end
    
    -- Main function, is running constantly with delta t time interval, functions esentially like an infinite while loop
    --
    function update(dt)
      -- lowers cooldown with each cycle
      if (self.closeCooldown > 0) then
        self.closeCooldown = self.closeCooldown - dt
      end
      if (self.openCooldown > 0) then
        self.openCooldown = self.openCooldown - dt
      end
    
      self.interacted = false
      --everything remaining is used to make doors automatic, and therefore should be skipped
      --when automatic functionality is undesirable.  No automatic when wired to input 1, opened by wire,
      --don't need automatic functionality when door opened from ANY wire input or locked
      if (((storage.wireControlled or storage.wireOpened) and not self.npcClosed) or storage.noAuto or storage.locked) then
        return
      elseif self.npcClosed and storage.wireControlled and (self.openCooldown <= 0) then
        --onInputNodeChange()
        setDirectionNPC()
        onInputMultiNodeChange()  --should open the door if still approriate per wire input at that point
        self.closeCooldown = 0.1
        self.openCooldown = 0.1
        self.npcClosed = false
        return
      end
    
      local objectIdsOpen = world.entityQuery(self.queryArea[1], self.queryArea[2], {
      withoutEntityId = entity.id(),
      includedTypes = storage.scanTargets,
      boundMode = storage.boundVar})
      
      if (#objectIdsOpen == 0) then
        -- resetting toggle once player gets out of range
        storage.playerClosed = false
      
        if not self.noClearOpen then
          --found some doors in missions with only wired outputs!
          --this will prevent doors with wired outputNode(0) from autoClosing when player opened
          storage.playerOpened = false
        end
      end
    
      autoOpen(objectIdsOpen)
      autoClose(objectIdsOpen)
    end
    
    
    function autoOpen(objectIdsOpen)
      if (#objectIdsOpen == 0) or storage.playerClosed or storage.state or (self.openCooldown > 0) then
        return
      end
      -- query for player at door proximity
      local playerPosition = world.entityPosition(objectIdsOpen[1])
      -- sb.loginfo("Player detected!")
      -- open door in direction depending on position of the player
      
      if not storage.isHorizontal then
        realOpenDoor(playerPosition[1] - storage.doorPosition[1])
        storage.playerOpened = false
        self.closeCooldown = 0.1
        -- sb.loginfo("direction: %d", playerPosition[1] - object.position()[1])
      else
        realOpenDoor(playerPosition[2] - storage.doorPosition[2])
        storage.playerOpened = false
        self.closeCooldown = 0
        --added a small timer
        -- sb.loginfo("direction: %d", playerPosition[1] - object.position()[1])
      end
    end
    
    
    function autoClose(objectIdsOpen)
      if (self.closeCooldown > 0) or not storage.state or (#objectIdsOpen > 0) or storage.playerOpened then
        return
      end
      -- check for NPCs in a smaller radius
      -- was initially queryRadius - 3, but npcs ended getting stuck in doors as they closed too early, (especially retractable wooden door), so now it is only -1
      local npcIds = world.npcQuery(storage.doorPosition, storage.queryRadius - 1, {boundMode = storage.boundVar})
      
      -- prevents door spasming
      if (#npcIds > 0) and (not storage.isHorizontal) then
        return
      end
      
    --disable for NPC's, close when opened by player
      realCloseDoor()
      storage.playerClosed = false
    end
    
    function setDirectionNPC()
      --special case function corrects direction if NPC opened door or will be nearest when opening
      -- was initially queryRadius - 3, but npcs ended getting stuck in doors as they closed too early, (especially retractable wooden door), so now it is only -1
      local npcIds = world.npcQuery(storage.doorPosition, storage.queryRadius - 1, {boundMode = storage.boundVar})
      if (#npcIds == 0) then
        return --in theory an NPC may move before this is called
      end
      local npcPosition = world.entityPosition(npcIds[1])
    
      if not storage.isHorizontal then
        setDirection((npcPosition[1] - storage.doorPosition[1]))
      else
        setDirection((npcPosition[2] - storage.doorPosition[2]))
      end
    end
    
    
    Be sure to check also special doors that retract vertically, like the retractable wooden door, as this one caused npcs to spasm before I changed the radius value in 2.8.
    I've made myself a playerfile specifically for testing everything regarding the doors:
    20160804164154_1.jpg
    I don't actually know how to copy a playerfile with all stuff, but if you do and want to have the savefile, tell me and I shall give it to you.

    Oh, and I've tested the "nps aren't stupid" mod, and it works great, the npcs can finally properly use horizontal doors with it and walk all around my testship now! I suggest you to use it too when testing, and in general to anyone playing, as it makes it much less of a hassle.
     
    bk3k likes this.
  2. bk3k

    bk3k Oxygen Tank

    So anyhow I threw up a new version on Github that seems pretty good and passed every test I threw at it. For those whom are curious to look under the hood.

    Also I wrote this for modders. More than just Automatic Doors. Because you can do some extra stuff with your doors given this mod too. Pretty versatile I think. But some of that info is pending an actual update release. Lornlynx may want to do his own testing, and maybe change some stuff, etc. So you'll get the update... when you get the update. :p

    Anyhow you'll see some of those features in action soon. Or maybe I should just release a "good enough for now version" now of some blocks and objects. Yeah I think I will. After a quick PNGGauntlet pass.
     
  3. lornlynx

    lornlynx Cosmic Narwhal

    lornlynx updated Automatic Doors with a new update entry:

    -big code rewrite by bk3k

    Read the rest of this update entry...
     
  4. lornlynx

    lornlynx Cosmic Narwhal

  5. lornlynx

    lornlynx Cosmic Narwhal

  6. CatofRiddles

    CatofRiddles Subatomic Cosmonaut

    I was just wondering... is there a way to edit this mod to target npcs instead of the player? I love this mod for the reason that it forces NPCs to close doors, but the radius that affects the player is too sensitive.
     
  7. bk3k

    bk3k Oxygen Tank

    You can indeed mod the mod.
    Change the radius(set at 5 tiles)
    Change the default scan targets(set for "player", "vehicle")
    It also separately scans for "NPC" before closing. The NPCs themselves don't do the door closing.

    Besides editing the code itself, If you make your own mod doors(or use Starcheat on vanilla doors), you can control basically every aspect of this behavior on that specific door.
    Say you want a trapdoor hatch for monsters only, you can do that.
    Decide that particular type of door should not be automatic, you can do that.
    Don't want it to react to vehicles... you can do that.
    Designate a specific boxed area to scan... wait I think the current version doesn't have that. But it should at the next update.

    Also included is support for additional animation states that vanilla doesn't support.
    You can find out about most of this with that link a few posts up.

    There will probably be some other options soon-ish. Such as letting you build a console that has an interface. It could change the default behavior for doors on that planet. Something you could affect from within-game rather than manually modify.

    Sadly I don't think it is possible to just inject a menu into the regular options. It would be possible though to use Manipulated UI instead of making a console.
    Advantages :
    *You don't need another object to mess with(crafting, carrying, placing/removing, turning into "perfectlygenericitem" when you go onto a *server without this mod).
    *You could change the settings any time, any place.
    *Probably a smoother option.

    Disadvantages :
    *That functionality would require an external mod. Not as a "required" mod, but the interface would be inaccessible without.
    *That menu should probably be disabled when on a dungeon/instance world, thus increasing the complexity of the interface's design.
    *If Manipulated UI becomes outdated/abandoned, that functionality will be gone and people will probably claim THIS mod is broken :facepalm:

    Adding a "required" mod to this mod - which already has a large user base(over 12k on Steam) - is unacceptable (in my opinion). So that also begs the question if the interface itself (in whatever ultimate form) should be a separate mod.
     
    lornlynx likes this.
  8. lornlynx

    lornlynx Cosmic Narwhal

    lornlynx updated Automatic Doors with a new update entry:

    3.7

    Read the rest of this update entry...
     
  9. lornlynx

    lornlynx Cosmic Narwhal

    Customization:
    With version 3.7 I have added a new way that should ease it for people to change certain values and behaviour, especially adaption of radius for opening doors was often requested.

    If you want to change these values, either unpack the .pak file from the Starbound mod forums into your Starbound/mods/ folder using the assets_unpacker.exe in Starbound/win_32, or alternatively download the raw unpacked mod files from here: https://www.mediafire.com/file/8dg8zt7tkyygo1m/AutomaticDoors_v3.8_raw.zip/file. Be sure to unsubscribe from the Steam workshop version and delete the automaticDoors.pak in the mods folder.

    Then you will find a file called "automaticDoors_default-values.lua" in the now unpacked automaticDoors folder.
    Open it in a text-editor, and you can find some basic values which you can now change to your likings.
    Keep in mind that you might need to replace doors for the changes to take effect.
     
    Last edited: Apr 25, 2021
  10. lornlynx

    lornlynx Cosmic Narwhal

  11. lornlynx

    lornlynx Cosmic Narwhal

  12. lornlynx

    lornlynx Cosmic Narwhal

Share This Page