Jump to content

Restricting code/perk to a specific character?


Recommended Posts

Hi, guys! I'm back, hoping to understand some more lua. So far, I've gotten by with hours of roaming these forums and cutting and pasting snippets together, but one of my recent issues is when a perk goes into modmain, it tends to work.. for the entire server.

What, roughly, do I have to do to be able to lock it to a specific character? I know it involves overriding a function and checking for a character tag, but I'm not sure how to reliably write that up.

Sharing code is honestly super embarrassing, because I'm sure my coding looks like a hot mess with how much trial and error and tinkering there is, but practice makes perfect, I suppose.

Example 1:

-- farming bonus
	TUNING.SEEDS_GROW_TIME = 4*TUNING.SEEDS_GROW_TIME/6
	TUNING.FARM1_GROW_BONUS = TUNING.FARM1_GROW_BONUS/2
	TUNING.FARM2_GROW_BONUS = TUNING.FARM1_GROW_BONUS/2
	TUNING.FARM3_GROW_BONUS = TUNING.FARM1_GROW_BONUS/2
	TUNING.POOP_FERTILIZE = TUNING.POOP_FERTILIZE*1.5
	TUNING.POOP_SOILCYCLES = TUNING.POOP_SOILCYCLES*1.5
	TUNING.GUANO_FERTILIZE = TUNING.GUANO_FERTILIZE*1.5
	TUNING.GUANO_SOILCYCLES = TUNING.GUANO_SOILCYCLES*1.5

This one confuses me. It's a farming bonus so you can essentially fertilize with manure way faster than usual. It's under master_postinit in Character A's prefab file, but affects Character B when playing together. I referenced this mod a ton, so I'm sure I may have made an error somewhere. Maybe in modmain?

Example 2:
 

AddPrefabPostInit("petals_evil", function(inst)
	local function OnDeploy(inst, pt) 
	local flower = GLOBAL.SpawnPrefab("planted_flower")
	if flower and prefab ~= "eliyth" then
	flower:PushEvent("growfrombutterfly")
	flower.Transform:SetPosition(pt:Get())
	inst.components.stackable:Get():Remove()	
	end
	end
	inst:AddComponent("deployable")
	inst:AddComponent("pollinator")
	inst.components.deployable.ondeploy = OnDeploy
	inst.components.deployable:SetDeployMode(GLOBAL.DEPLOYMODE.PLANT)
end)

This one's extra embarrassing because I have the gut feeling it probably is just... wrong, but it gets the job done, which is planting evil petals into regular flowers.
I referenced this forum post, but it's an older code from 2015 and was hard to understand when everything is squashed all on one line. I tried the "local old_Deploy" function given, but the log just comes back at me with an error in attempting to index "inst" (not sure of the specific line, I've been swapping codes in and out for awhile, haha.) I also got this specific code from there, but it wouldn't work when copy pasted so I did a lot of tinkering to try and make sense of what I should be doing.

(The prefab = "eliyth" thing was my poor attempt at seeing if that would work for ensuring it only works on that character. Coding is never that simple, unfortunately)

These are specific examples, but that said, I'd be eternally thankful if there were any tutorials or tips on how to ensure a code works for one specific character, regardless of the function. Sorry for any confusing/messy code :?. Thank you in advance!

Link to comment
Share on other sites

TUNING is a global table from where the server fetches values. If your character exists and edits that table, all other characters benefit. This isn't an issue in Don't Starve single-player because your character doesn't exist with others in the same world instance.

Which means that you either have to make a mechanic unique to the character (give him a special mechanic that replaces fertilize with values unique to him), or to intercept the fertilization at a point where you can define who's doing the fertilizing.

Where to do the interception only comes with experience and creativity.

Lets go step by step.

TUNING.POOP_FERTILIZE = TUNING.POOP_FERTILIZE * 1.5
TUNING.POOP_SOILCYCLES = TUNING.POOP_SOILCYCLES * 1.5
TUNING.GUANO_FERTILIZE = TUNING.GUANO_FERTILIZE * 1.5
TUNING.GUANO_SOILCYCLES = TUNING.GUANO_SOILCYCLES * 1.5

These values get attached to fertilizer when the fertilizer is spawned in the world.

We want to edit items in particular.

-- reference to global table
local TUNING = GLOBAL.TUNING

local bonuses = {
    poop = {
        value = 1.5,
        soil = 1.5,
    },
    guano = {
        value = 1.5,
        soil = 1.5,
    },
}

local function improveFertilizer(doer, item)
    -- see if it's our character
    if doer and doer.prefab == "eliyth" then
        -- see if item is fertilizer
        if item and item.components.fertilizer then
            -- get value or empty table if there's no value
            local bonus = bonuses[item.prefab] or {}
            -- get value or value that changes nothing
            local bonus1 = bonus.value or 1
            local bonus2 = bonus.soil or 1
            item.components.fertilizer.fertilizervalue = item.components.fertilizer.fertilizervalue * bonus1
            item.components.fertilizer.soil_cycles = item.components.fertilizer.soil_cycles * bonus2
        end
    end
end

local function restoreFertilizer(doer, item)
    if doer and doer.prefab == "eliyth" then
        -- item did what it had to do, now time to go back to normal
        -- check if it's valid, it may have been one single poop (removed) instead of a stack of it (still valid)
        if item and item.components.fertilizer and item:IsValid() then
            local bonus = bonuses[item.prefab] or {}
            local bonus1 = bonus.value or 1
            local bonus2 = bonus.soil or 1
            -- division instead of multiplication
            item.components.fertilizer.fertilizervalue = item.components.fertilizer.fertilizervalue / bonus1
            item.components.fertilizer.soil_cycles = item.components.fertilizer.soil_cycles / bonus2
        end
    end
end


-- save old action function
local old_FERTILIZE_fn = GLOBAL.ACTIONS.FERTILIZE.fn

GLOBAL.ACTIONS.FERTILIZE.fn = function(act)
    -- improve item if it deserves it
    improveFertilizer(act.doer, act.invobject)
    -- do the action of fertilizing
    local ret = old_FERTILIZE_fn(act)
    -- restore item if it needs it
    restoreFertilizer(act.doer, act.invobject)
    -- return if action went well
    return ret
end

This in modmain should do the trick.

What did those values do? Edit the effectiveness of fertilizer. The higher the bonus, the less items you use to fertilize. One poop equals two.

 

... now here's an interesting problem.

TUNING.SEEDS_GROW_TIME = TUNING.SEEDS_GROW_TIME * (4 / 6)
TUNING.FARM1_GROW_BONUS = TUNING.FARM1_GROW_BONUS / 2
TUNING.FARM2_GROW_BONUS = TUNING.FARM1_GROW_BONUS / 2
TUNING.FARM3_GROW_BONUS = TUNING.FARM1_GROW_BONUS / 2

These values alter not only the effectiveness of fertilizer, but the effectiveness of farm plot and the speed of seed growth. Those are unrelated values to fertilizing. Those are also applied globally. The question is: how does our character relate to those values? Food grows faster, but what did our character do about it? Did he make the farmplot? Did he fertilize the farmplot? Does the bonus apply forever after our character interacts with the farmplot? Many questions to ask... yet useless, because what you want is just to be more efficient fertilizing. You should only touch the fertilizing values.

Instead of making fertilizer more efficient by making the crops requiring less time, make fertilizer more efficient by making it more potent. So instead of implementing the TUNING modification, editing the bonuses is enough.

local bonuses = {
    poop = {
        value = 3,
        soil = 1.5,
    },
    guano = {
        value = 3,
        soil = 1.5,
    },
}

Value is for how much time they skip. Soil is for how much soil they renew (you can't plant forever in farms unless you put poop in the dirt).

Of course, if you want faster farms, then you've a design issue to tackle more than a code issue.

 

 

For the flower planting, you can do it like this.

In modmain.lua, we do the deployable stuff:

-- this table loads files in scripts/prefabs inside your mod folder
-- this way you can include prefab files for yourself
PrefabFiles = {
    "petals_evil_placer",
}


-- function from the butterfly file
local function OnDeploy(inst, pt, deployer)
    -- if our character deploys, spawn; otherwise just complain
    if deployer.prefab == "eliyth" then
        -- spawn flower
        local flower = GLOBAL.SpawnPrefab("flower_evil")
        -- set flower position to deployed position
        flower.Transform:SetPosition(pt:Get())
        -- remove the petal deployed
        inst.components.stackable:Get():Remove()
    else
        -- complain
        deployer.components.talker:Say("I should find Eliyth for this!")
        -- return petal to inventory
        deployer.components.inventory:GiveItem(inst.components.stackable:Get())
    end
end


-- for evil petal entities
AddPrefabPostInit("petals_evil", function(inst)
    -- if instance is server, the deployable component exists, edit it then
    if GLOBAL.TheWorld.ismastersim then
        -- make it deployable
        inst:AddComponent("deployable")
        inst.components.deployable.ondeploy = OnDeploy
        inst.components.deployable:SetDeployMode(GLOBAL.DEPLOYMODE.PLANT)
    end
end)

And in your scripts/prefabs folder you will have a petals_evil_placer.lua file that will contain this:

return MakePlacer("petals_evil_placer", "flower_petals_evil", "flower_petals_evil", "anim")

(Just like how butterfly has a butterfly_placer inside the file, but we don't have one of those for petals.)

Link to comment
Share on other sites

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
 Share

×
  • Create New...