Jump to content

Recommended Posts

Hey everyone,

So for Freya, I wanted to give her the Lazy Forager effect (of picking up items).

However, I've tried to go to the amulet.lua files and copy over the lazy forager effect to my name.lua file and I haven't gotten anything working yet. If anybody could help me out with this, it would be greatly appreciated.

Edited by LisaMcRad
1 hour ago, Thomas Die said:

Who's Freya and how have you copied of the code and can you show me it.

1. The character I've been working on


2. I've literally copied this portion from the orange amulet portion of the amulet.lua file. It was placed in the freya.lua file just before the revive code.
 

local function pickup(inst, owner)
    if owner == nil or owner.components.inventory == nil then
        return
    end
    local x, y, z = owner.Transform:GetWorldPosition()
    local ents = TheSim:FindEntities(x, y, z, TUNING.ORANGEAMULET_RANGE, ORANGE_PICKUP_MUST_TAGS, ORANGE_PICKUP_CANT_TAGS)
    local ba = owner:GetBufferedAction()
    for i, v in ipairs(ents) do
        if v.components.inventoryitem ~= nil and
            v.components.inventoryitem.canbepickedup and
            v.components.inventoryitem.cangoincontainer and
            not v.components.inventoryitem:IsHeld() and
            owner.components.inventory:CanAcceptCount(v, 1) > 0 and
            (ba == nil or ba.action ~= ACTIONS.PICKUP or ba.target ~= v) then

            if owner.components.minigame_participator ~= nil then
                local minigame = owner.components.minigame_participator:GetMinigame()
                if minigame ~= nil then
                    minigame:PushEvent("pickupcheat", { cheater = owner, item = v })
                end
            end

            --Amulet will only ever pick up items one at a time. Even from stacks.
            SpawnPrefab("sand_puff").Transform:SetPosition(v.Transform:GetWorldPosition())

            inst.components.finiteuses:Use(1)

            local v_pos = v:GetPosition()
            if v.components.stackable ~= nil then
                v = v.components.stackable:Get()
            end

            if v.components.trap ~= nil and v.components.trap:IsSprung() then
                v.components.trap:Harvest(owner)
            else
                owner.components.inventory:GiveItem(v, nil, v_pos)
            end
            return
        end
    end
end

The only thing I've seen is that it doesn't work. It's maybe that I'm missing something else to go along with it, maybe something to activate the effect passively (like an aura?)

First and foremost, check that ORANGE_PICKUP_CANT_TAGS and ORANGE_PICKUP_MUST_TAGS do exists in your file, and are correctly copied, since they are local to amulet.lua.

Then, if you copied the code for the Lazy Forager and changed nothing to it, and have no crashes, I think this would be linked to calling your special function every x frames. The copied code needs to be called and can be only executed once, unless you create a task that runs periodically with that function.

Check the code below ; the lazy forager function, whose effect are periodically called with DoPeriodicTask :

local function onequip_orange(inst, owner)
    owner.AnimState:OverrideSymbol("swap_body", "torso_amulets", "orangeamulet")
    inst.task = inst:DoPeriodicTask(TUNING.ORANGEAMULET_ICD, pickup, nil, owner)
end

local function onunequip_orange(inst, owner)
    owner.AnimState:ClearOverrideSymbol("swap_body")
    if inst.task ~= nil then
        inst.task:Cancel()
        inst.task = nil
    end
end

The onequip_orange function is the one called when your character equips the amulet, and the onunequip_orange is the one when you remove it. Since the amulet can be equipped, it has the "equippable" component attached to it, which contains OnEquip and OnUnequip methods, and those will call the specified ones for the lazy forager as seen with those lines :

inst.components.equippable:SetOnEquip(onequip_orange)
inst.components.equippable:SetOnUnequip(onunequip_orange)

And that's how the effect is triggered when equipping the amulet.

In your case, if the effect is meant to be automatic like the amulet, you will also need to create a periodic task. Otherwise, if it's an action (like Wortox' soul hop), no need to make a periodic task, but you will have to create a custom action. I don't know how to create one, but some mods did (Ex: Warfarin with dashes), so you can check how they managed to do it.

Edited by Nelzanger
  • Like 1
2 hours ago, Nelzanger said:

First and foremost, check that ORANGE_PICKUP_CANT_TAGS and ORANGE_PICKUP_MUST_TAGS do exists in your file, and are correctly copied, since they are local to amulet.lua.

Then, if you copied the code for the Lazy Forager and changed nothing to it, and have no crashes, I think this would be linked to calling your special function every x frames. The copied code needs to be called and can be only executed once, unless you create a task that runs periodically with that function.

Check the code below ; the lazy forager function, whose effect are periodically called with DoPeriodicTask :


local function onequip_orange(inst, owner)
    owner.AnimState:OverrideSymbol("swap_body", "torso_amulets", "orangeamulet")
    inst.task = inst:DoPeriodicTask(TUNING.ORANGEAMULET_ICD, pickup, nil, owner)
end

local function onunequip_orange(inst, owner)
    owner.AnimState:ClearOverrideSymbol("swap_body")
    if inst.task ~= nil then
        inst.task:Cancel()
        inst.task = nil
    end
end

The onequip_orange function is the one called when your character equips the amulet, and the onunequip_orange is the one when you remove it. Since the amulet can be equipped, it has the "equippable" component attached to it, which contains OnEquip and OnUnequip methods, and those will call the specified ones for the lazy forager as seen with those lines :


inst.components.equippable:SetOnEquip(onequip_orange)
inst.components.equippable:SetOnUnequip(onunequip_orange)

And that's how the effect is triggered when equipping the amulet.

In your case, if the effect is meant to be automatic like the amulet, you will also need to create a periodic task. Otherwise, if it's an action (like Wortox' soul hop), no need to make a periodic task, but you will have to create a custom action. I don't know how to create one, but some mods did (Ex: Warfarin with dashes), so you can check how they managed to do it.

It's supposed to be an automatic task like the amulet, but I'm also having trouble with that. Mostly since I can't find any good API documents for DST.

The main issue I'm having is an error where the game crashes after I load in since it doesn't know any variables like self or the owner tag from the Orange Amulet code. I found it odd it didn't know self since I thought that would be a global variable?

Maybe it could be due to me puting the DoPeriodicTask in the master_postinit function. Maybe not, I'm not sure.

I've uploaded the file here if anybody would like to check.

freya.lua

I've looked at your file. I can't be sure at 100% because I also struggle with what I'm manipulating at times, because we don't have an API, IDE's powerful features (autocompletion, debugger, etc.) and lua does not really help since it's too high level and does not propose strong typing (I love/hate lua about that), but I'll try to guide you where the issue should be.

 

Let's check your master post init with the following line :

inst:DoPeriodicTask(1.0, pickup, nil, owner)

First, the line above will never stops, and you will have no way to access the task. If you want to do so, to pause or stop it, I strongly suggest to put your task in a variable like the following, only if pickup has only one parameter, which is inst :

-- Create a task that is kept into pickupTask variable, belonging to your instance.
inst.pickupTask = inst:DoPeriodicTask(1.0, pickup) 

 

Then we have your pickup method. However, we know that the pickup function is for an amulet at the origin, which is an equippable and inventory item. Therefore, the amulet needs to have access to two parameters : 

  • inst, the amulet instance itself, to change the amulet variables, like the finite uses ;
  • owner, the owner of the amulet, aka, the player prefab when wearing it, to apply effect to the player, or, accordingly to its actions/position.

Back to master post init : since the parameter in master post init is "inst", the inst is probably your character prefab itself. In that case, you can already see a contradiction here in your own context, by wanting the player to pickup, and not an equippable object.

Since you have already access to the player instance, you already know the "owner" : it's your prefab instance itself. And the "amulet instance" here does not exists because your player is not an amulet, so the following line makes no sense for your player :

-- Makes no sense because inst is supposed here to be an amulet, containing finiteuses component to reduce its usage
inst.components.finiteuses:Use(1)

Therefore, the signature for your method should be the following :

-- Copied and pasted from amulet.lua : Wrong because inst is expected to be an amulet, and the owner, the player prefab
local function pickup(inst, owner)

-- Your pickup function with your own context : Makes more sense because inst is the player prefab, so you can manipulate it already
local function pickup(inst)

And your pickup function should look like this, with all "owner" replaced by "inst" :

-- Pickup Stuff
local ORANGE_PICKUP_MUST_TAGS = { "_inventoryitem" }
local ORANGE_PICKUP_CANT_TAGS = { "INLIMBO", "NOCLICK", "knockbackdelayinteraction", "catchable", "fire", "minesprung", "mineactive" }
local function pickup(inst)
    if inst == nil or inst.components.inventory == nil then
        return
    end
    local x, y, z = inst.Transform:GetWorldPosition()
    local ents = TheSim:FindEntities(x, y, z, TUNING.ORANGEAMULET_RANGE, ORANGE_PICKUP_MUST_TAGS, ORANGE_PICKUP_CANT_TAGS)
    local ba = inst:GetBufferedAction()
    for i, v in ipairs(ents) do
        if v.components.inventoryitem ~= nil and
            v.components.inventoryitem.canbepickedup and
            v.components.inventoryitem.cangoincontainer and
            not v.components.inventoryitem:IsHeld() and
            inst.components.inventory:CanAcceptCount(v, 1) > 0 and
            (ba == nil or ba.action ~= ACTIONS.PICKUP or ba.target ~= v) then

            if inst.components.minigame_participator ~= nil then
                local minigame = inst.components.minigame_participator:GetMinigame()
                if minigame ~= nil then
                    minigame:PushEvent("pickupcheat", { cheater = inst, item = v })
                end
            end

            --Amulet will only ever pick up items one at a time. Even from stacks.
            SpawnPrefab("sand_puff").Transform:SetPosition(v.Transform:GetWorldPosition())

            local v_pos = v:GetPosition()
            if v.components.stackable ~= nil then
                v = v.components.stackable:Get()
            end

            if v.components.trap ~= nil and v.components.trap:IsSprung() then
                v.components.trap:Harvest(owner)
            else
                inst.components.inventory:GiveItem(v, nil, v_pos)
            end
            return
        end
    end
end

 

Feel free to correct me if I'm wrong. Again, this kind of issues arise quickly in lua because we don't know easily what we are manipulating : it requires more digging inside the methods, but also, their context of execution.

Edited by Nelzanger
Edit code comments so coloration would hopefully work
  • Like 2

 

2 hours ago, Nelzanger said:

I've looked at your file. I can't be sure at 100% because I also struggle with what I'm manipulating at times, because we don't have an API, IDE's powerful features (autocompletion, debugger, etc.) and lua does not really help since it's too high level and does not propose strong typing (I love/hate lua about that), but I'll try to guide you where the issue should be.

 

Let's check your master post init with the following line :


inst:DoPeriodicTask(1.0, pickup, nil, owner)

First, the line above will never stops, and you will have no way to access the task. If you want to do so, to pause or stop it, I strongly suggest to put your task in a variable like the following, only if pickup has only one parameter, which is inst :


-- Create a task that is kept into pickupTask variable, belonging to your instance.
inst.pickupTask = inst:DoPeriodicTask(1.0, pickup) 

 

Then we have your pickup method. However, we know that the pickup function is for an amulet at the origin, which is an equippable and inventory item. Therefore, the amulet needs to have access to two parameters : 

  • inst, the amulet instance itself, to change the amulet variables, like the finite uses ;
  • owner, the owner of the amulet, aka, the player prefab when wearing it, to apply effect to the player, or, accordingly to its actions/position.

Back to master post init : since the parameter in master post init is "inst", the inst is probably your character prefab itself. In that case, you can already see a contradiction here in your own context, by wanting the player to pickup, and not an equippable object.

Since you have already access to the player instance, you already know the "owner" : it's your prefab instance itself. And the "amulet instance" here does not exists because your player is not an amulet, so the following line makes no sense for your player :


-- Makes no sense because inst is supposed here to be an amulet, containing finiteuses component to reduce its usage
inst.components.finiteuses:Use(1)

Therefore, the signature for your method should be the following :


-- Copied and pasted from amulet.lua : Wrong because inst is expected to be an amulet, and the owner, the player prefab
local function pickup(inst, owner)

-- Your pickup function with your own context : Makes more sense because inst is the player prefab, so you can manipulate it already
local function pickup(inst)

And your pickup function should look like this, with all "owner" replaced by "inst" :


-- Pickup Stuff
local ORANGE_PICKUP_MUST_TAGS = { "_inventoryitem" }
local ORANGE_PICKUP_CANT_TAGS = { "INLIMBO", "NOCLICK", "knockbackdelayinteraction", "catchable", "fire", "minesprung", "mineactive" }
local function pickup(inst)
    if inst == nil or inst.components.inventory == nil then
        return
    end
    local x, y, z = inst.Transform:GetWorldPosition()
    local ents = TheSim:FindEntities(x, y, z, TUNING.ORANGEAMULET_RANGE, ORANGE_PICKUP_MUST_TAGS, ORANGE_PICKUP_CANT_TAGS)
    local ba = inst:GetBufferedAction()
    for i, v in ipairs(ents) do
        if v.components.inventoryitem ~= nil and
            v.components.inventoryitem.canbepickedup and
            v.components.inventoryitem.cangoincontainer and
            not v.components.inventoryitem:IsHeld() and
            inst.components.inventory:CanAcceptCount(v, 1) > 0 and
            (ba == nil or ba.action ~= ACTIONS.PICKUP or ba.target ~= v) then

            if inst.components.minigame_participator ~= nil then
                local minigame = inst.components.minigame_participator:GetMinigame()
                if minigame ~= nil then
                    minigame:PushEvent("pickupcheat", { cheater = inst, item = v })
                end
            end

            --Amulet will only ever pick up items one at a time. Even from stacks.
            SpawnPrefab("sand_puff").Transform:SetPosition(v.Transform:GetWorldPosition())

            local v_pos = v:GetPosition()
            if v.components.stackable ~= nil then
                v = v.components.stackable:Get()
            end

            if v.components.trap ~= nil and v.components.trap:IsSprung() then
                v.components.trap:Harvest(owner)
            else
                inst.components.inventory:GiveItem(v, nil, v_pos)
            end
            return
        end
    end
end

 

Feel free to correct me if I'm wrong. Again, this kind of issues arise quickly in lua because we don't know easily what we are manipulating : it requires more digging inside the methods, but also, their context of execution.

Thank you so much! This is exactly what I needed!

I'm still a bit new to Lua and DST modding in general so I appreciate all the help you've been giving me!

I'll def credit you when the mod releases!

  • Like 1

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
×
  • Create New...