Jump to content

targeting green staff


Recommended Posts

I have working code allowing me to place any item with durability into a science machine and get SOMETHING back, like a gold nugget. now that that seems to be working fine, my next and second to last test is to replicate the functions of green staff apon inserting an item into the science machine.

however when i instruct the game to preform the functions of the green staff code after putting the item in the machine, the game crashes saying lua:23 (the line that says "local recipe = GetRecipe(target.prefab)") attempt to call global 'GetRecipe' (a nil value)

 

my hunch is that this "target" function is set up in a different file and that the reason it is crashing is because the item put into the science machine does not count as the target, so the game is wondering where the target is, and crashes. however i do not know how to set the item put into the machine to be the target.

 

mostly was wondering how or where the "target" thing (i don't really know what to call it) in the file for staffs, specifically the green staff, works and where its function is defined.

 

the green staff code that seems to work with "target" code i am talking about is this

-----

local function candestroy(staff, caster, target)
    if not target then return false end

    local recipe = GetRecipe(target.prefab)

    return recipe ~= nil
end

-----

local function destroystructure(staff, target)

    local ingredient_percent = 1

    if target.components.finiteuses then
        ingredient_percent = target.components.finiteuses:GetPercent()
    elseif target.components.fueled and target.components.inventoryitem then
        ingredient_percent = target.components.fueled:GetPercent()
    elseif target.components.armor and target.components.inventoryitem then
        ingredient_percent = target.components.armor:GetPercent()
    end

    local recipe = GetRecipe(target.prefab)

    local caster = staff.components.inventoryitem.owner

    local loot = {}

    if recipe then       
        for k,v in ipairs(recipe.ingredients) do
            if not string.find(v.type, "gem") then
                local amt = math.ceil(v.amount * ingredient_percent)
                for n = 1, amt do
                    table.insert(loot, v.type)
                end
            end
        end
    end

    if #loot <= 0 then
        return
    end

    for k,v in pairs(loot) do
        SpawnLootPrefab(target, v)
    end

    target:Remove()
    
end

-----

 

The code for my mod is as follows

local function candestroy(target)
    if not target then return false end

    local recipe = GetRecipe(target.prefab)

    return recipe ~= nil
end

local function destroystructure(target)

    local ingredient_percent = 1

    if target.components.finiteuses then
        ingredient_percent = target.components.finiteuses:GetPercent()
    elseif target.components.fueled and target.components.inventoryitem then
        ingredient_percent = target.components.fueled:GetPercent()
    elseif target.components.armor and target.components.inventoryitem then
        ingredient_percent = target.components.armor:GetPercent()
    end

    local recipe = GetRecipe(target.prefab)

    local loot = {}

    if recipe then       
        for k,v in ipairs(recipe.ingredients) do
            if not string.find(v.type, "gem") then
                local amt = math.ceil(v.amount * ingredient_percent)
                for n = 1, amt do
                    table.insert(loot, v.type)
                end
            end
        end
    end

    if #loot <= 0 then
        return
    end

    local sounds = {}
    sounds = getsoundsforstructure(staff, target)
    for k,v in pairs(sounds) do
        print("playing ",v)
        staff.SoundEmitter:PlaySound(v)
    end

    for k,v in pairs(loot) do
        SpawnLootPrefab(target, v)
    end

    target:Remove()
    
end

-- accept

local function OnGetItemFromPlayer(inst, giver, item)
    inst.components.prototyper.onactivate()
    inst:DoTaskInTime(1.5, destroystructure)
end

local function AcceptTest(inst, item, giver)
    return item.components.finiteuses or item.components.armor or item.components.fueled
end

-- recycler:
local function recycleengine(inst)
    if not inst.components.lootdropper then
        inst:AddComponent("lootdropper")
    end

    inst:AddComponent("trader")
    inst.components.trader:SetAcceptTest(AcceptTest)
    inst.components.trader.onaccept = OnGetItemFromPlayer
end

AddPrefabPostInit("researchlab", recycleengine)

-- trading
local function MakeTradable(inst)
    if not inst.components.tradable then
        inst:AddComponent("tradable")
    end
end

AddComponentPostInit("finiteuses", function(self)
    local inst = self.inst
    MakeTradable(inst)
end)

AddComponentPostInit("armor", function(self)
    local inst = self.inst
    MakeTradable(inst)
end)

AddComponentPostInit("fueled", function(self)
    local inst = self.inst
    MakeTradable(inst)
end)

 

i would appreciate any help, thank you.

 

Link to comment
Share on other sites

thank you, that solves one issue. however, now i am getting an error on lua:43 which is

SpawnLootPrefab(target, v) 

the error being "attempt to call global 'SpawnLootPrefab'  (a nil value)"

as i said before, my guess is it is not registering the item put into the science machine as the target, and this it doesnt preform the recipie and percent "getting" on the item, so it doesn't know what to spawn.

i don't know how to set the input item as the target

Link to comment
Share on other sites

I did that, but on the same line of code it says

Variable 'SpawnLootPrefab' is not declared 

once again i am left thinking that it doesn't know what item to make "target" and generate the recipe ingredients of.

Link to comment
Share on other sites

My fault, it's not global, it's a local function in the staff.lua file:

Spoiler

local function SpawnLootPrefab(inst, lootprefab)
    if lootprefab then
        local loot = SpawnPrefab(lootprefab)
        if loot then
            
            local pt = Point(inst.Transform:GetWorldPosition())           
            
            loot.Transform:SetPosition(pt.x,pt.y,pt.z)
            
            if loot.Physics then
            
                local angle = math.random()*2*PI
                loot.Physics:SetVel(2*math.cos(angle), 10, 2*math.sin(angle))

                if loot.Physics and inst.Physics then
                    pt = pt + Vector3(math.cos(angle), 0, math.sin(angle))*(loot.Physics:GetRadius() + inst.Physics:GetRadius())
                    loot.Transform:SetPosition(pt.x,pt.y,pt.z)
                end
                
                loot:DoTaskInTime(1, 
                    function() 
                        if not (loot.components.inventoryitem and loot.components.inventoryitem:IsHeld()) then
                            if not loot:IsOnValidGround() then
                                local fx = SpawnPrefab("splash_ocean")
                                local pos = loot:GetPosition()
                                fx.Transform:SetPosition(pos.x, pos.y, pos.z)
                                --PlayFX(loot:GetPosition(), "splash", "splash_ocean", "idle")
                                if loot:HasTag("irreplaceable") then
                                    loot.Transform:SetPosition(GetPlayer().Transform:GetWorldPosition())
                                else
                                    loot:Remove()
                                end
                            end
                        end
                    end)
            end
            
            return loot
        end
    end
end

 

You have to copy it into modmain too to properly drop the items.

Link to comment
Share on other sites

Thanks, i added that and also gave 

PI

and

Vector3

a global prefix to stop crashinh,  but trying to see if my mod worked an interesting side effect happned

the upon inserting my axe into the science machine, the machine itself blew apart!

i guess that makes sense if you throw tools into moving components, but that's not exactly what i was going for.

at this point i am almost certain the issue is that the science machine is being set as "target", when i want the item put into it to be the "target"

Link to comment
Share on other sites

Just now, troltos said:

at this point i am almost certain the issue is that the science machine is being set as "target", when i want the item put into it to be the "target"

Yes, you are right. You can do either of the following:

Spoiler
  1. Use a wraper function of sorts
        inst:DoTaskInTime(1.5,function() destroystructure(target) end)
  2. Refer to the target to begin with
        target:DoTaskInTime(1.5, destroystructure)
  3. Give a second parameter to the function
        inst:DoTaskInTime(1.5, destroystructure, target) --for this, you will need to add "inst" as first parameter of the destroy function
Link to comment
Share on other sites

they all crash in differnent ways, im assuming all im supposed to do is change what i previously had

local function OnGetItemFromPlayer(inst, giver, item)
    inst.components.prototyper.onactivate()
    inst:DoTaskInTime(1.5, destroystructure)
end

to either

local function OnGetItemFromPlayer(inst, giver, item)
    inst.components.prototyper.onactivate()
    inst:DoTaskInTime(1.5,function() destroystructure(target) end)
end

or

local function OnGetItemFromPlayer(inst, giver, item)
    inst.components.prototyper.onactivate()
    target:DoTaskInTime(1.5, destroystructure)
end

 

The first method gives me an error 

lua:57 which is the start of

if target.components.finiteuses then
        ingredient_percent = target.components.finiteuses:GetPercent()
    elseif target.components.fueled and target.components.inventoryitem then
        ingredient_percent = target.components.fueled:GetPercent()
    elseif target.components.armor and target.components.inventoryitem then
        ingredient_percent = target.components.armor:GetPercent()
    end

the error being "attempt to index local 'target' (a nil value)

 

and the second method gives me an error of

lua:96 which the line of "target:DoTaskInTime(1.5, destroystructure)
the error being "attempt to index local 'target' (a nil value)

 

 

 

 

Link to comment
Share on other sites

1 hour ago, troltos said:

the error being "attempt to index local 'target' (a nil value)

Woops, the "target" is called "item" in that function. Just use "item" instead of "target". (Every function names the stuff you give it, this one calls it "item" and the destroy function calls it "target". It is important to remember what is what.)

Link to comment
Share on other sites

alright, i switched in

local function OnGetItemFromPlayer(inst, giver, item)
    inst.components.prototyper.onactivate()
    item:DoTaskInTime(1.5, destroystructure)
end

and then i am getting

lua:26

at

pt = pt + GLOBAL.Vector3(math.cos(angle), 0, math.sin(angle))*(loot.Physics:GetRadius() + inst.Physics:GetRadius())

the error being

attempt to preform arithmetic on a nil value

 

i tried switching

pt = pt + GLOBAL.Vector3(math.cos(angle), 0, math.sin(angle))*(loot.Physics:GetRadius() + inst.Physics:GetRadius())

to 

pt = pt + GLOBAL.Vector3(math.cos(angle), 0, math.sin(angle))*(loot.Physics:GetRadius() + item.Physics:GetRadius())

and

local function SpawnLootPrefab(inst, lootprefab)

to

local function SpawnLootPrefab(inst, item, lootprefab)

and that fixed the crash, but it did nothing when it played the prototyping animation.

 

Link to comment
Share on other sites

local function DropEverythingOnItem(item)
	if item.components.inventory then
		item.components.inventory:DropEverything()
	end

	if item.components.container then
		item.components.container:DropEverything()
	end
end

local function GenerateRecycledLoot(item)
	local ingredient_percent = 1

	if item.components.finiteuses then
		ingredient_percent = item.components.finiteuses:GetPercent()
	elseif item.components.armor then
		ingredient_percent = item.components.armor:GetPercent()
	elseif item.components.fueled then
		ingredient_percent = item.components.fueled:GetPercent()
	end

	local recipe = GLOBAL.GetRecipe(item.prefab)

	local loot = {}

	if recipe then
		for k, v in ipairs(recipe.ingredients) do
			if not string.find(v.type, "gem") then
				local amt = math.ceil(v.amount * ingredient_percent)
				for n = 1, amt do
					table.insert(loot, v.type)
				end
			end
		end
	end

	return loot
end

local function SpawnRecycledLoot(inst, loot)
	for k, v in pairs(loot) do
		inst.components.lootdropper:SpawnLootPrefab(v)
	end
end

local function OnGetItemFromPlayer(inst, giver, item)
	inst.components.prototyper.onactivate()
	DropEverythingOnItem(item)
	local loot = GenerateRecycledLoot(item)
	inst:DoTaskInTime(1.5, SpawnRecycledLoot, loot)
end

local function AcceptTest(inst, item, giver)
	return item.components.finiteuses or item.components.armor or item.components.fueled
end

-- recycler:
local function recycleengine(inst)
	if not inst.components.lootdropper then
		inst:AddComponent("lootdropper")
	end

	inst:AddComponent("trader")
	inst.components.trader:SetAcceptTest(AcceptTest)
	inst.components.trader.onaccept = OnGetItemFromPlayer
end

AddPrefabPostInit("researchlab", recycleengine)

-- trading
local function MakeTradable(inst)
	if not inst.components.tradable then
		inst:AddComponent("tradable")
	end
end

AddComponentPostInit("finiteuses", function(self)
	local inst = self.inst
	MakeTradable(inst)
end)

AddComponentPostInit("armor", function(self)
	local inst = self.inst
	MakeTradable(inst)
end)

AddComponentPostInit("fueled", function(self)
	local inst = self.inst
	MakeTradable(inst)
end)

 

Link to comment
Share on other sites

Thank you, that seems great for replicate the functions of the green staff!

i figured once i got a replications of the function of the green staff it would be pretty simple to do a minor tweak, simply to get rid of the whole "always give at least one of the component" feature of the green staff

looking through the code, i couldn't determine what instructed the game to always give back one instead of just following the % chance of the items durability, so i decided to test the numerical values.

 

local ingredient_percent = 1

seems to do absolutely nothing regardless of what number i set it to, it didn't even have an effect if i removed the line entirely.

 

with

for n = 1, amt do

setting the number to zero gives you at least 2 of every component, even if there was only one of that component in the recipie.

setting the number to above one will take away that many - 1 from whatever you would have gotten.

for example, setting it to 3 and then recycling a 100% marble suit will give you back  10 marble and 2 rope (meaning it took away 2 marble and 2 rope)

Link to comment
Share on other sites

2 hours ago, troltos said:

i couldn't determine what instructed the game to always give back one

local amt = math.ceil(v.amount * ingredient_percent)

math.ceil() ALWAYS rounds up. To ALWAYS round down, use math.floor(). To round "properly", you can add 0.5 and subtract it later again.

Link to comment
Share on other sites

hmm, is there a way, rather than just rounding, to have it be a % chance? like so putting a 95% axe in the science machine would have a 95% chance to drop a twig and a 95% chance to drop a flint. 

i had always assumed that was similar to how the green staff worked, but i realize now that it is actually just the amount required of an ingredient in a recipe multiplied by the % durability.

Link to comment
Share on other sites

	if recipe then
		for k, v in ipairs(recipe.ingredients) do
			if math.random() <= ingredient_percent then
				local amt = math.ceil(v.amount)
				for n = 1, amt do
					table.insert(loot, v.type)
				end
			end
		end
	end

ingredient_percent goes from 0-1, math.random() vomits a number between 0-1 with uniform distribution.

So if percent is 0.95, then you have a 95% chance to get the ingredient used, per ingredient.

Link to comment
Share on other sites

that is definitely better than before, and works perfectly with items that only have a single number of each ingredient, but after some tests it seems to be "all or nothing" with ingredients, for example, putting a 50% weather pain into the machine, i would have a 50% chance to get a gear, a 50% chance to get a goat horn, and a 50% chance to get all 10 feathers back. but if i don't get all 10 of them back, i get none back at all. 

Link to comment
Share on other sites

Well, I assumed you wanted it per ingredient type, since the example only had 1 of each type.

Code would look like:

	if recipe then
		for k, v in ipairs(recipe.ingredients) do
			local amt = math.ceil(v.amount)
			for n = 1, amt do
				if math.random() <= ingredient_percent then
					table.insert(loot, v.type)
				end
			end
		end
	end

 

Link to comment
Share on other sites

Archived

This topic is now archived and is closed to further replies.

Please be aware that the content of this thread may be outdated and no longer applicable.

×
  • Create New...