Modding Help Scripted Tool

Discussion in 'Starbound Modding' started by Augustus Lupfil, Jul 18, 2021.

  1. Augustus Lupfil

    Augustus Lupfil Space Spelunker

    Is there any way to script tool interaction with tiles/objects? I have looked through "beamaxe.beamaxe", "beamaxe.lua" and "copperpickaxe.miningtool", but it doesn't help. For example, I would like to create a tool, which can interact with objects only, but has no effect, when affects tiles. Is it possible?
     
  2. bk3k

    bk3k Oxygen Tank

    You'd want an active item. You can scan for things at the location of the cursor when clicking.
    Code:
    #### `List<EntityId>` world.entityQuery(`Vec2F` position, `Variant<Vec2F, float` positionOrRadius, [`Json` options])
    
    Queries for entities in a specified area of the world and returns a list of their entity ids. Area can be specified either as the `Vec2F` lower left and upper right positions of a rectangle, or as the `Vec2F` center and `float` radius of a circular area. The following additional parameters can be specified in options:
    
    * __withoutEntityId__ - Specifies an `EntityId` that will be excluded from the returned results
    * __includedTypes__ - Specifies a list of one or more `String` entity types that the query will return. In addition to standard entity type names, this list can include "mobile" for all mobile entity types or "creature" for players, monsters and NPCs.
    * __boundMode__ - Specifies the bounding mode for determining whether entities fall within the query area. Valid options are "position", "collisionarea", "metaboundbox". Defaults to "collisionarea" if unspecified.
    * __order__ - A `String` used to specify how the results will be ordered. If this is set to "nearest" the entities will be sorted by ascending distance from the first positional argument. If this is set to "random" the list of results will be shuffled.
    * __callScript__ - Specifies a `String` name of a function that should be called in the script context of all scripted entities matching the query.
    * __callScriptArgs__ - Specifies a list of arguments that will be passed to the function called by callScript.
    * __callScriptResult__ - Specifies a `LuaValue` that the function called by callScript must return; entities whose script calls do not return this value will be excluded from the results. Defaults to `true`.
    
    ---
    Similarly you can use world.objectQuery() since you only want objects. If you where trying to damage only objects, you could them spawn a projectile at that point, which may or may not be visible depending upon your preferences.

    If you are trying to do something else, you can use the messaging system. Or as you see, the world.entityQuery() or world.objectQuery() can call a script from that object too. An example of an object that uses this is doors.
    /objects/wired/door/door.lua
    Code:
    function hasCapability(capability)
      if capability == 'lockedDoor' then
        return storage.locked
      elseif object.isInputNodeConnected(0) or storage.locked or self.sensorConfig 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
    So to show you how that gets used,
    /scripts/actions/movement.lua
    Code:
    function openDoors(args, board)
      local direction = args.direction or mcontroller.facingDirection() --Default to opening doors in front
    
      local position = mcontroller.position()
      local bounds = rect.translate(mcontroller.boundBox(), mcontroller.position())
      local opened = true
      if direction > 0 then
        bounds[1] = bounds[3]
        bounds[3] = bounds[3] + args.distance
      else
        bounds[3] = bounds[1]
        bounds[1] = bounds[1] - args.distance
      end
      if world.rectTileCollision(bounds, {"Dynamic"}) then
        opened = false
    
        -- There is a colliding object in the way. See if we can open it
        local closedDoors = world.entityQuery(rect.ll(bounds), rect.ur(bounds), { includedTypes = {"object"}, callScript = "hasCapability", callScriptArgs = { "closedDoor" } })
        util.debugRect(bounds, "blue")
        if args.openLocked then
          local lockedDoors = world.entityQuery(rect.ll(bounds), rect.ur(bounds), { includedTypes = {"object"}, callScript = "hasCapability", callScriptArgs = { "lockedDoor" } })
          closedDoors = util.mergeLists(closedDoors, lockedDoors)
        end
        for _, doorId in pairs(closedDoors) do
          local toDoor = world.distance(world.entityPosition(doorId), mcontroller.position())
          if toDoor[1] * direction > 0 then
            world.callScriptedEntity(doorId, "openDoor")
            opened = true
          end
        end
      end
    
      return opened
    end
    So that will scan for objects in the rectangle in front of the entity itself. It will then call
    hasCapability() - which only is gonna work on objects that have that function call. If the function call doesn't exist or if it doesn't get a return of true, then world.entityQuery() won't return that particular object.

    They are then using world.callScriptedEntity() to call openDoor() - itself within doors.lua
    So you see there are multiple ways for one entity to trigger the function call of another.
     
    Augustus Lupfil likes this.
  3. Augustus Lupfil

    Augustus Lupfil Space Spelunker

    Thank you, your answer is great. I have tried something like it before, but I made it through .minningtool, fireableItem. It works the same, I think. I set "tileDamage" to 0 to disable default resource extracting. I used world.entityQuery and fireableItem.ownerAimPosition to get an entity, a player is aiming at. But it doesn't really work accurate. The player was aiming at the dirt tile under a tree and grass, but world.entityQuery said, there are three entities: the plyaer themself, the tree and some grass. Maybe, I am doing something wrong about it? Is there any way, I can know what is the only one entity, player aiming at? world.objectAt doesn't work, because a tree isn't an object, I tried it.
     

Share This Page