Jump to content

Recommended Posts

so i'm trying to make a mod that checks how many souls wortox has, and when it reaches a certain number, it will drop the excess soul to avoid getting more than 20 souls, and losing sanity due to soul overload.

that my code

local _G = GLOBAL

local MAX = GetModConfigData("MAXSOULS")

local function IsSoul(item)
    return item.prefab == "wortox_soul"
end

local function GetStackSize(item)
    return item.components.stackable ~= nil and item.components.stackable:StackSize() or 1
end

local function TheDrop(parent)
    local souls = parent.components.inventory:FindItems(IsSoul)
    local count = 0

    for i, v in pairs(_G.ThePlayer.replica.inventory:GetItems(IsSoul)) do
        if v ~= nil and v.prefab == "wortox_soul" then
            for i, v in ipairs(souls) do
            count = count + GetStackSize(v)
            end
            if count > MAX then
                _G.ThePlayer.replica.inventory:DropItemFromInvTile(v)
            end
        end
    end
end

AddPlayerPostInit(function(inst)
    inst:DoPeriodicTask(0.5, function()
        if 1==1 then
            TheDrop(inst)
        end
    end)
end)

 

it works fine in local worlds, but when i add caves or go to a server the games crashes

thats the log

[00:28:15]: [string "../mods/_Wortox Souls Droping/modmain.lua"]:16: attempt to index field 'inventory' (a nil value)
LUA ERROR stack traceback:
    ../mods/_Wortox Souls Droping/modmain.lua:16 in (upvalue) TheDrop (Lua) <15-29>
    ../mods/_Wortox Souls Droping/modmain.lua:34 in (field) fn (Lua) <32-36>
    scripts/scheduler.lua:177 in (method) OnTick (Lua) <155-207>
    scripts/scheduler.lua:371 in (global) RunScheduler (Lua) <369-377>
    scripts/update.lua:191 in () ? (Lua) <170-249>

this is my first forum post, so sorry if i did something wrong, and ty for you time

Edited by Helwdrokin

is i tried to search for a function that could replace FindItems() but i couldn't find one
i really don't know what to do, i think only if make a function that could replace finditems but idk what self (in inventory.lua) and inst (in general) exactly do

First, some coding context, if you are interested.
 

Spoiler

 

Lua is lenient on "typing" your variables, aka weakly-typed, meaning that you don't need to declare what type your variable is. This is both a blessing and a curse, because you can use this at your advantage for tricky (and unsafe) parts, but you also don't know what you are manipulating. Knowing the context in where the code is used (what method was called, what are the parameters given, etc.), is important because if you do something wrong, the game will crash, and crashes, along with the logs, are the only thing you can rely on for debugging and understanding what went wrong.

Consider this definition in C#, with a strong-typed signature :


public List<Entity> FindFoodEntities(List<Enity> entities, bool mode) 

This code tells you what you are manipulating just by looking at types :

  • public List<Entity> says that your method can be called by an instance of your object and will return a list of objects named "Entity" ;
  • List<Enity> entities says that your first parameter is a list of entities, probably the one you wish to loop on to find the food related ones ;
  • bool mode says that you have a toggle, true or false, for a special processing, for example, finding all the food, or only those who are not spoiled.

Consider the same method definition in Lua, which is weakly-typed :


local function FindFoodEntities(inst, entities, mode)

You can't know what your parameters are, and you don't know what is the return type. You also have an additional parameter, which is "inst". This is how lua works. You just know by looking at it that it will "find entities". To understand what your method is doing, you are obliged to see where this method is used in a practical situation. And yet, you may still need to check further to know what the parameters are made from !

All this to say that in Lua, context is important in order to understand how the code works, because it is not explicitly shown. You will need to dig in lots of files to get a grasp on how things are called and used. And also, why the additional inst ? In Lua, you need to give your own instance as parameter to make effective changes on it. Otherwise, changes will not be applied.

 

 

Then, onto your question about Inventory.self and inst in general :

  • Inventory is a class. But Lua does not have classes, that's why Klei had the need to emulate its behavior by making class.lua. A class, when created, will reference its own instance, often with a keyword, like "this" in C#, or for our context, "self". With Klei's class, "self" is a variable that contains the instance of the class you created. So when applied to inventory, "self" will reference the instance of the inventory itself. That allows to call methods, or set variables on your own object in other methods without the need to give the instance as a parameter ;
  • "inst" in general, is the instance of an object you are manipulating. In Lua, if your object does not behavior like a class, you need to give the instance as a parameter if you want your changes to be effective on the said instance, otherwise, your changes will not be applied.

 

Finally, I don't think you need to make new functions. You can hook the one that manages the soul drop when going over 20 souls.

Let's check how it's managed in wortox.lua :

--prefabs/wortox.lua
local function CheckSoulsAdded(inst)
    inst._checksoulstask = nil
    local souls = inst.components.inventory:FindItems(IsSoul) -- Find souls in the inventory
    local count = 0
  
    -- Compute how many souls you have in the inventory
    for i, v in ipairs(souls) do
        count = count + GetStackSize(v)
    end
  
    -- If you have reached the threshold, then you drop souls
    if count > TUNING.WORTOX_MAX_SOULS then
        --convert count to drop count
        count = count - math.floor(TUNING.WORTOX_MAX_SOULS / 2) + math.random(0, 2) - 1
        table.sort(souls, SortByStackSize)
        local pos = inst:GetPosition()
       
        -- Drop X souls at pos
        for i, v in ipairs(souls) do
            local vcount = GetStackSize(v)
            if vcount < count then
                inst.components.inventory:DropItem(v, true, true, pos)
                count = count - vcount
            else
                if vcount == count then
                    inst.components.inventory:DropItem(v, true, true, pos)
                else
                    v = v.components.stackable:Get(count)
                    v.Transform:SetPosition(pos:Get())
                    v.components.inventoryitem:OnDropped(true)
                end
                break
            end
        end
    
        -- Sanity penalty
        inst.components.sanity:DoDelta(-TUNING.SANITY_MEDLARGE)
        inst:PushEvent("souloverload")
    
    -- If you have 80% of souls on your max allowed, push the event that will probably make Wortox says that he has enough
    elseif count > TUNING.WORTOX_MAX_SOULS * .8 then
        inst:PushEvent("soultoomany")
    end
end

 

You can see the part where wortox drops souls if he goes above a specific amount. All you need would be to "hook" this function and redefine its behavior into what you want.

Edited by Nelzanger
5 hours ago, Nelzanger said:

You can see the part where wortox drops souls if he goes above a specific amount. All you need would be to "hook" this function and redefine its behavior into what you want.

The problem is that this should be a client side mod, but the changes you propose make it an server mod.

local _G = GLOBAL

local MAX = GetModConfigData("MAXSOULS")

local function GetStackSize(item)
    return item.replica.stackable ~= nil and item.replica.stackable:StackSize() or 1
end

local function TheDrop(inst)
    local count = 0
    for i, v in pairs(inst.replica.inventory:GetItems()) do
        if v ~= nil and v.prefab == "wortox_soul" then
            count = count + GetStackSize(v)
            end
            if count > MAX then
                inst.replica.inventory:DropItemFromInvTile(v)
            end
        end
    end
end

Something like this should be able to run completely on the client side without needing to add your own code on the server.

  • Like 2

it worked Monti18, I just had to use an if statement to check which inventory slot the soul was in before dropping it, otherwise the character would end up dropping the last item in the inventory

i am realy glad for the help ty Monti18

and ty Nelzanger you realy helped me understand how lua works

  • Like 1
  • Thanks 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...