Jump to content

Exposed (for proper modding) loot for tumbleweeds and graves


Recommended Posts

Like some other things... the functions and tables related to tumbleweed loot and grave mounds loot are all defined as local, and therefore are inaccessible to the outside/mods. So there's no way to merely add/remove/change a loot item, mods must duplicate at least some of the existing code of the prefab's functions, including the whole existing loot list, to affect the loot, which isn't so good for compatibility and robustness, as that will has to be re-done every time that part of the game was updated (as just happened in the latest update) and the mod re-released, otherwise the official update will be overwritten or worse, something may be broken in some way (due to "partially updated" code), and multiple mods using this method will also generally completely overwrite each other.

So this is a suggestion to change tumbleweed.lua and mound.lua to make the loot generation functions and loot lists available to the outside in some fashion. Ideally for other modding interests the same would be the case for any other general loot lists that aren't changable in a good way which I'm not aware of (random chests? ruins chests?). There's a global PickRandomTrinket() in trinkets.lua used for grave mounds which is a good start, but the rest of the loot and logic are still local in mound.lua.

For example, the relevant parts of tumbleweed.lua look like this (seems to me like there's a bit of unused stuff in that file, by the way):
 

Spoiler

...
...
...

local prefabs =
{
    "splash_ocean",
    "tumbleweedbreakfx",
    "ash",
    "cutgrass",
    "twigs",
    "petals",
    "foliage",
    "silk",
    "rope",
    "seeds",
    "purplegem",
    "bluegem",
    "redgem",
    "orangegem",
    "yellowgem",
    "greengem",
    "seeds",
    "trinket_6",
    "cutreeds",
    "feather_crow",
    "feather_robin",
    "feather_robin_winter",
    "feather_canary",
    "trinket_3",
    "beefalowool",
    "rabbit",
    "mole",
    "butterflywings",
    "fireflies",
    "beardhair",
    "berries",
    "TOOLS_blueprint",
    "LIGHT_blueprint",
    "SURVIVAL_blueprint",
    "FARM_blueprint",
    "SCIENCE_blueprint",
    "WAR_blueprint",
    "TOWN_blueprint",
    "REFINE_blueprint",
    "MAGIC_blueprint",
    "DRESS_blueprint",
    "petals_evil",
    "trinket_8",
    "houndstooth",
    "stinger",
    "gears",
    "spider",
    "frog",
    "bee",
    "mosquito",
    "boneshard",
}

local CHESS_LOOT =
{
    "chesspiece_pawn_sketch",
    "chesspiece_muse_sketch",
    "chesspiece_formal_sketch",
    "trinket_15", --bishop
    "trinket_16", --bishop
    "trinket_28", --rook
    "trinket_29", --rook
    "trinket_30", --knight
    "trinket_31", --knight
}

for k, v in ipairs(CHESS_LOOT) do
    table.insert(prefabs, v)
end

...
...
...

local function MakeLoot(inst)
    local possible_loot =
    {
        {chance = 40,   item = "cutgrass"},
        {chance = 40,   item = "twigs"},
        {chance = 1,    item = "petals"},
        {chance = 1,    item = "foliage"},
        {chance = 1,    item = "silk"},
        {chance = 1,    item = "rope"},
        {chance = 1,    item = "seeds"},
        {chance = 0.01, item = "purplegem"},
        {chance = 0.04, item = "bluegem"},
        {chance = 0.02, item = "redgem"},
        {chance = 0.02, item = "orangegem"},
        {chance = 0.01, item = "yellowgem"},
        {chance = 0.02, item = "greengem"},
        {chance = 1,    item = "seeds"},
        {chance = 0.5,  item = "trinket_6"},
        {chance = 0.5,  item = "trinket_4"},
        {chance = 1,    item = "cutreeds"},
        {chance = 0.33, item = "feather_crow"},
        {chance = 0.33, item = "feather_robin"},
        {chance = 0.33, item = "feather_robin_winter"},
        {chance = 0.33, item = "feather_canary"},
        {chance = 1,    item = "trinket_3"},
        {chance = 1,    item = "beefalowool"},
        {chance = 0.1,  item = "rabbit"},
        {chance = 0.1,  item = "mole"},
        {chance = 0.1,  item = "spider", aggro = true},
        {chance = 0.1,  item = "frog", aggro = true},
        {chance = 0.1,  item = "bee", aggro = true},
        {chance = 0.1,  item = "mosquito", aggro = true},
        {chance = 1,    item = "butterflywings"},
        {chance = .02,  item = "beardhair"},
        {chance = 1,    item = "berries"},
        {chance = 0.1,    item = "TOOLS_blueprint"},
        {chance = 0.1,    item = "LIGHT_blueprint"},
        {chance = 0.1,    item = "SURVIVAL_blueprint"},
        {chance = 0.1,    item = "FARM_blueprint"},
        {chance = 0.1,    item = "SCIENCE_blueprint"},
        {chance = 0.1,    item = "WAR_blueprint"},
        {chance = 0.1,    item = "TOWN_blueprint"},
        {chance = 0.1,    item = "REFINE_blueprint"},
        {chance = 0.1,    item = "MAGIC_blueprint"},
        {chance = 0.1,    item = "DRESS_blueprint"},
        {chance = 1,    item = "petals_evil"},
        {chance = 1,    item = "trinket_8"},
        {chance = 1,    item = "houndstooth"},
        {chance = 1,    item = "stinger"},
        {chance = 1,    item = "gears"},
        {chance = 0.1,  item = "boneshard"},
    }

    local chessunlocks = TheWorld.components.chessunlocks
    if chessunlocks ~= nil then
        for i, v in ipairs(CHESS_LOOT) do
            if not chessunlocks:IsLocked(v) then
                table.insert(possible_loot, { chance = .1, item = v })
            end
        end
    end

    local totalchance = 0
    for m, n in ipairs(possible_loot) do
        totalchance = totalchance + n.chance
    end

    inst.loot = {}
    inst.lootaggro = {}
    local next_loot = nil
    local next_aggro = nil
    local next_chance = nil
    local num_loots = 3
    while num_loots > 0 do
        next_chance = math.random()*totalchance
        next_loot = nil
        next_aggro = nil
        for m, n in ipairs(possible_loot) do
            next_chance = next_chance - n.chance
            if next_chance <= 0 then
                next_loot = n.item
                if n.aggro then next_aggro = true end
                break
            end
        end
        if next_loot ~= nil then
            table.insert(inst.loot, next_loot)
            if next_aggro then 
                table.insert(inst.lootaggro, true)
            else
                table.insert(inst.lootaggro, false)
            end
            num_loots = num_loots - 1
        end
    end
end

...
...
...

local function fn()
    local inst = CreateEntity()

    ...

    MakeLoot(inst)

	...

    return inst
end

 

They could be reconstructed to something more extensible, along these lines (don't know if it's totally right as directly working code):

Spoiler

...
...
...

local prefabs =
{
    "splash_ocean",
    "tumbleweedbreakfx",
}

TUNING.TUMBLEWEED_CHESS_LOOT = TUNING.TUMBLEWEED_CHESS_LOOT or
{
    "chesspiece_pawn_sketch",
    "chesspiece_muse_sketch",
    "chesspiece_formal_sketch",
    "trinket_15", --bishop
    "trinket_16", --bishop
    "trinket_28", --rook
    "trinket_29", --rook
    "trinket_30", --knight
    "trinket_31", --knight
}

for k, v in pairs(TUNING.TUMBLEWEED_CHESS_LOOT) do
    table.insert(prefabs, v)
end

TUNING.TUMBLEWEED_BASE_LOOT = TUNING.TUMBLEWEED_BASE_LOOT or
{
	{chance = 40,   item = "cutgrass"},
	{chance = 40,   item = "twigs"},
	{chance = 1,    item = "petals"},
	{chance = 1,    item = "foliage"},
	{chance = 1,    item = "silk"},
	{chance = 1,    item = "rope"},
	{chance = 1,    item = "seeds"},
	{chance = 0.01, item = "purplegem"},
	{chance = 0.04, item = "bluegem"},
	{chance = 0.02, item = "redgem"},
	{chance = 0.02, item = "orangegem"},
	{chance = 0.01, item = "yellowgem"},
	{chance = 0.02, item = "greengem"},
	{chance = 1,    item = "seeds"},
	{chance = 0.5,  item = "trinket_6"},
	{chance = 0.5,  item = "trinket_4"},
	{chance = 1,    item = "cutreeds"},
	{chance = 0.33, item = "feather_crow"},
	{chance = 0.33, item = "feather_robin"},
	{chance = 0.33, item = "feather_robin_winter"},
	{chance = 0.33, item = "feather_canary"},
	{chance = 1,    item = "trinket_3"},
	{chance = 1,    item = "beefalowool"},
	{chance = 0.1,  item = "rabbit"},
	{chance = 0.1,  item = "mole"},
	{chance = 0.1,  item = "spider", aggro = true},
	{chance = 0.1,  item = "frog", aggro = true},
	{chance = 0.1,  item = "bee", aggro = true},
	{chance = 0.1,  item = "mosquito", aggro = true},
	{chance = 1,    item = "butterflywings"},
	{chance = .02,  item = "beardhair"},
	{chance = 1,    item = "berries"},
	{chance = 0.1,    item = "TOOLS_blueprint"},
	{chance = 0.1,    item = "LIGHT_blueprint"},
	{chance = 0.1,    item = "SURVIVAL_blueprint"},
	{chance = 0.1,    item = "FARM_blueprint"},
	{chance = 0.1,    item = "SCIENCE_blueprint"},
	{chance = 0.1,    item = "WAR_blueprint"},
	{chance = 0.1,    item = "TOWN_blueprint"},
	{chance = 0.1,    item = "REFINE_blueprint"},
	{chance = 0.1,    item = "MAGIC_blueprint"},
	{chance = 0.1,    item = "DRESS_blueprint"},
	{chance = 1,    item = "petals_evil"},
	{chance = 1,    item = "trinket_8"},
	{chance = 1,    item = "houndstooth"},
	{chance = 1,    item = "stinger"},
	{chance = 1,    item = "gears"},
	{chance = 0.1,  item = "boneshard"},
}

for k,v in pairs(TUNING.TUMBLEWEED_BASE_LOOT) do
    table.insert(prefabs, t.item)
end

TUNING.TUMBLEWEED_NUM_LOOTS = TUNING.TUMBLEWEED_NUM_LOOTS or 3

...
...
...

function ConstructTumbleweedLoot(baseloot)
	local loot = deepcopy(baseloot)
	local chessunlocks = TheWorld.components.chessunlocks
    if chessunlocks ~= nil then
        for k, v in pairs(TUNING.TUMBLEWEED_CHESS_LOOT) do
            if not chessunlocks:IsLocked(v) then
                table.insert(loot, { chance = .1, item = v })
            end
        end
    end
	return loot
end

function PickTumbleweedLoot(inst,possible_loot)
    local totalchance = 0
    for m, n in pairs(possible_loot) do
        totalchance = totalchance + n.chance
    end
	local next_loot = nil
    local next_aggro = nil
    local next_chance = nil
    local num_loots = TUNING.TUMBLEWEED_NUM_LOOTS
    while num_loots > 0 do
        next_chance = math.random()*totalchance
        next_loot = nil
        next_aggro = nil
        for m, n in pairs(possible_loot) do
            next_chance = next_chance - n.chance
            if next_chance <= 0 then
                next_loot = n.item
                if n.aggro then next_aggro = true end
                break
            end
        end
        if next_loot ~= nil then
            table.insert(inst.loot, next_loot)
            if next_aggro then 
                table.insert(inst.lootaggro, true)
            else
                table.insert(inst.lootaggro, false)
            end
            num_loots = num_loots - 1
        end
    end
end

function MakeTumbleweedLoot(inst,baseloot)
    local possible_loot = ConstructTumbleweedLoot(baseloot)

    inst.loot = {}
    inst.lootaggro = {}
	PickTumbleweedLoot(inst,possible_loot)
    
end

...
...
...

local function fn()
    local inst = CreateEntity()

    ...

    MakeTumbleweedLoot(inst,TUNING.TUMBLEWEED_BASE_LOOT)

	...

    return inst
end

 

I guess I also I think it'd be better if the loot list would be transformed to a simpler {prefabname = chance} table instead (aggro flag could be kept track of elsewhere, like in another table {prefabwithaggroname = true} ,or in a general prefabs flags list if more flags like aggro are planned {prefabwithflagsname = {aggro = true, otherflag = 3} } ) and the tumbleweed's loot was determined on pickup rather than on creation. But ignoring that, with the current way of things and with the above code changes as an example, mods could then robustly modify the loot like so:

AddSimPostInit(function() --tumbleweed.lua is loaded after modmain.lua, postinit won't be needed if TUNING lines and function definitions are moved to an earlier loaded file like tuning.lua

local loot = TUNING.TUMBLEWEED_BASE_LOOT

local remove_items = table.invert{"cutgrass", "twigs", "petals"} -- tumbleweeds don't give these items anymore

local function strends(String,End)
   return End=='' or string.sub(String,-string.len(End))==End
end

for k,v in pairs(loot) do
	if remove_items[v.item] then
		table.remove(loot,k) -- remove the item's entry from the loot table
	elseif strends(v.item,"gem")
		v.chance = 5 -- change the relative chance of all gems to 5%, instead of the original
	end
end

table.insert( loot, {item = "butter", chance = 10} ) -- add butter as possible loot, with 10% relative chance



-- conditional/more complex loot modification:

-- override the original function
local orig_construct = GLOBAL.ConstructTumbleweedLoot
GLOBAL.ConstructTumbleweedLoot = function(...)
	local ret = orig_construct(...)
	if GLOBAL.TheWorld.state.season  == "winter" then
		table.insert(ret, {item = "ice", chance = 20} )	-- tumblweed may drop ice in winter only
	elseif GLOBAL.TheWorld.state.season  == "summer" then
		table.insert(ret, {item = "cactus_flower", chance = 10} ) -- tumblweed may drop cactus flower in summer only
	end
	return ret
end

end)


Of course, it doesn't have to look exactly like that, the important thing is that the code is set up in some extensible fashion that makes the base loot list, loot list construction function and loot picking function (which are currently in the same, local function, ideally they'd be separated, like above) accessible and overridable. So, stored in some kind of global var or member var attached to the tumbleweed (in that case) prefab, which makes it at least changable with AddPrefabPostInit (kind of an inefficient way to make the same change to every single created prefab instance, but at least it works well).

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...