Jump to content

Recommended Posts

I have some experience with coding but always sucked at modding.
Would appreciate any help very much.
 
So the ultimate goal of the simple mod I wanna make as a first one to take a grasp on DS modding is:
1. Make trees drop branches when chopped down.
2. Remove fake saplings from the game completely. (I say "fake" because they never grow into trees)
3. Have the mod only affect the things mentioned so that it is somewhat version-independent and non-conflicting with other mods.
 
So far I achieved the first goal and with evergreens only.
 
 modinfo.lua

name = "BranchTwigs"description = "Lets you harvest branches from Evergreens as Twigs"author = "Erquint"version = "0.1"-- forumthread = ""api_version = 6-- icon_atlas = ""-- icon = ""-- priority = dont_starve_compatible =  truereign_of_giants_compatible = true

 scripts\prefabs\evergreens.lua

local assets ={    Asset("ANIM", "anim/evergreen_new.zip"), --build    Asset("ANIM", "anim/evergreen_new_2.zip"), --build    Asset("ANIM", "anim/evergreen_tall_old.zip"),    Asset("ANIM", "anim/evergreen_short_normal.zip"),    Asset("ANIM", "anim/dust_fx.zip"),    Asset("SOUND", "sound/forest.fsb"),}local prefabs ={    "log",    "twigs",    "pinecone",    "charcoal",    "leif",    "leif_sparse",    "pine_needles"}local builds = {	normal = {		file="evergreen_new",		prefab_name="evergreen",		normal_loot = {"log", "log", "pinecone", "twigs", "twigs"},		short_loot = {"log", "twigs"},		tall_loot = {"log", "log", "log", "pinecone", "pinecone", "twigs", "twigs", "twigs"},		drop_pinecones=true,		leif="leif",    },	sparse = {		file="evergreen_new_2",		prefab_name="evergreen_sparse",		normal_loot = {"log","log", "twigs", "twigs"},		short_loot = {"log", "twigs"},		tall_loot = {"log", "log","log", "twigs", "twigs", "twigs"},		drop_pinecones=false,		leif="leif_sparse",    },}local function makeanims(stage)    return {        idle="idle_"..stage,        sway1="sway1_loop_"..stage,        sway2="sway2_loop_"..stage,        chop="chop_"..stage,        fallleft="fallleft_"..stage,        fallright="fallright_"..stage,        stump="stump_"..stage,        burning="burning_loop_"..stage,        burnt="burnt_"..stage,        chop_burnt="chop_burnt_"..stage,        idle_chop_burnt="idle_chop_burnt_"..stage    }endlocal short_anims = makeanims("short")local tall_anims = makeanims("tall")local normal_anims = makeanims("normal")local old_anims ={	idle="idle_old",    sway1="idle_old",    sway2="idle_old",    chop="chop_old",    fallleft="chop_old",    fallright="chop_old",    stump="stump_old",    burning="idle_olds",    burnt="burnt_tall",    chop_burnt="chop_burnt_tall",    idle_chop_burnt="idle_chop_burnt_tall",}local function dig_up_stump(inst, chopper)	inst:Remove()	inst.components.lootdropper:SpawnLootPrefab("log")endlocal function chop_down_burnt_tree(inst, chopper)    inst:RemoveComponent("workable")    inst.SoundEmitter:PlaySound("dontstarve/forest/treeCrumble")              inst.SoundEmitter:PlaySound("dontstarve/wilson/use_axe_tree")          	inst.AnimState:PlayAnimation(inst.anims.chop_burnt)    RemovePhysicsColliders(inst)	inst:ListenForEvent("animover", function() inst:Remove() end)    inst.components.lootdropper:SpawnLootPrefab("charcoal")    inst.components.lootdropper:DropLoot()    if inst.pineconetask then        inst.pineconetask:Cancel()        inst.pineconetask = nil    endendlocal function GetBuild(inst)	local build = builds[inst.build]	if build == nil then		return builds["normal"]	end	return buildendlocal burnt_highlight_override = {.5,.5,.5}local function OnBurnt(inst, imm)		local function changes()	    if inst.components.burnable then		    inst.components.burnable:Extinguish()		end		inst:RemoveComponent("burnable")		inst:RemoveComponent("propagator")		inst:RemoveComponent("growable")		inst.components.lootdropper:SetLoot({})		if GetBuild(inst).drop_pinecones then			inst.components.lootdropper:AddChanceLoot("pinecone", 0.1)		end				if inst.components.workable then			inst.components.workable:SetWorkLeft(1)			inst.components.workable:SetOnWorkCallback(nil)			inst.components.workable:SetOnFinishCallback(chop_down_burnt_tree)		end	end			if imm then		changes()	else		inst:DoTaskInTime( 0.5, changes)	end    	inst.AnimState:PlayAnimation(inst.anims.burnt, true)    inst.AnimState:SetRayTestOnBB(true);    inst:AddTag("burnt")    inst.highlight_override = burnt_highlight_overrideendlocal function PushSway(inst)    if math.random() > .5 then        inst.AnimState:PushAnimation(inst.anims.sway1, true)    else        inst.AnimState:PushAnimation(inst.anims.sway2, true)    endendlocal function Sway(inst)    if math.random() > .5 then        inst.AnimState:PlayAnimation(inst.anims.sway1, true)    else        inst.AnimState:PlayAnimation(inst.anims.sway2, true)    endendlocal function SetShort(inst)    inst.anims = short_anims        if inst.components.workable then	    inst.components.workable:SetWorkLeft(TUNING.EVERGREEN_CHOPS_SMALL)	end		        inst.components.lootdropper:SetLoot(GetBuild(inst).short_loot)    Sway(inst)endlocal function GrowShort(inst)    inst.AnimState:PlayAnimation("grow_old_to_short")    inst.SoundEmitter:PlaySound("dontstarve/forest/treeGrowFromWilt")              PushSway(inst)endlocal function SetNormal(inst)    inst.anims = normal_anims        if inst.components.workable then	    inst.components.workable:SetWorkLeft(TUNING.EVERGREEN_CHOPS_NORMAL)	end	    inst.components.lootdropper:SetLoot(GetBuild(inst).normal_loot)    Sway(inst)endlocal function GrowNormal(inst)    inst.AnimState:PlayAnimation("grow_short_to_normal")    inst.SoundEmitter:PlaySound("dontstarve/forest/treeGrow")              PushSway(inst)endlocal function SetTall(inst)    inst.anims = tall_anims	if inst.components.workable then		inst.components.workable:SetWorkLeft(TUNING.EVERGREEN_CHOPS_TALL)	end	    inst.components.lootdropper:SetLoot(GetBuild(inst).tall_loot)    Sway(inst)endlocal function GrowTall(inst)    inst.AnimState:PlayAnimation("grow_normal_to_tall")    inst.SoundEmitter:PlaySound("dontstarve/forest/treeGrow")              PushSway(inst)endlocal function SetOld(inst)    inst.anims = old_anims		if inst.components.workable then	    inst.components.workable:SetWorkLeft(1)	end		if GetBuild(inst).drop_pinecones then		inst.components.lootdropper:SetLoot({"pinecone"})	else        inst.components.lootdropper:SetLoot({})    end    Sway(inst)endlocal function GrowOld(inst)    inst.AnimState:PlayAnimation("grow_tall_to_old")    inst.SoundEmitter:PlaySound("dontstarve/forest/treeWilt")              PushSway(inst)endlocal function inspect_tree(inst)    if inst:HasTag("burnt") then        return "BURNT"    elseif inst:HasTag("stump") then        return "CHOPPED"    endendlocal growth_stages ={    {name="short", time = function(inst) return GetRandomWithVariance(TUNING.EVERGREEN_GROW_TIME[1].base, TUNING.EVERGREEN_GROW_TIME[1].random) end, fn = function(inst) SetShort(inst) end,  growfn = function(inst) GrowShort(inst) end , leifscale=.7 },    {name="normal", time = function(inst) return GetRandomWithVariance(TUNING.EVERGREEN_GROW_TIME[2].base, TUNING.EVERGREEN_GROW_TIME[2].random) end, fn = function(inst) SetNormal(inst) end, growfn = function(inst) GrowNormal(inst) end, leifscale=1 },    {name="tall", time = function(inst) return GetRandomWithVariance(TUNING.EVERGREEN_GROW_TIME[3].base, TUNING.EVERGREEN_GROW_TIME[3].random) end, fn = function(inst) SetTall(inst) end, growfn = function(inst) GrowTall(inst) end, leifscale=1.25 },    {name="old", time = function(inst) return GetRandomWithVariance(TUNING.EVERGREEN_GROW_TIME[4].base, TUNING.EVERGREEN_GROW_TIME[4].random) end, fn = function(inst) SetOld(inst) end, growfn = function(inst) GrowOld(inst) end },}local function chop_tree(inst, chopper, chops)        if chopper and chopper.components.beaverness and chopper.components.beaverness:IsBeaver() then		inst.SoundEmitter:PlaySound("dontstarve/characters/woodie/beaver_chop_tree")          	else		inst.SoundEmitter:PlaySound("dontstarve/wilson/use_axe_tree")          	end    local fx = SpawnPrefab("pine_needles")    local x, y, z= inst.Transform:GetWorldPosition()    fx.Transform:SetPosition(x,y + math.random()*2,z)    inst.AnimState:PlayAnimation(inst.anims.chop)    inst.AnimState:PushAnimation(inst.anims.sway1, true)    	--tell any nearby leifs to wake up	local pt = Vector3(inst.Transform:GetWorldPosition())	local ents = TheSim:FindEntities(pt.x,pt.y,pt.z, TUNING.LEIF_REAWAKEN_RADIUS, {"leif"})	for k,v in pairs(ents) do		if v.components.sleeper and v.components.sleeper:IsAsleep() then			v:DoTaskInTime(math.random(), function() v.components.sleeper:WakeUp() end)		end		v.components.combat:SuggestTarget(chopper)	endendlocal function chop_down_tree(inst, chopper)    inst:RemoveComponent("burnable")    MakeSmallBurnable(inst)    inst:RemoveComponent("propagator")    MakeSmallPropagator(inst)    inst:RemoveComponent("workable")    inst.SoundEmitter:PlaySound("dontstarve/forest/treefall")    local pt = Vector3(inst.Transform:GetWorldPosition())    local hispos = Vector3(chopper.Transform:GetWorldPosition())    local he_right = (hispos - pt):Dot(TheCamera:GetRightVec()) > 0        if he_right then        inst.AnimState:PlayAnimation(inst.anims.fallleft)        inst.components.lootdropper:DropLoot(pt - TheCamera:GetRightVec())    else        inst.AnimState:PlayAnimation(inst.anims.fallright)        inst.components.lootdropper:DropLoot(pt + TheCamera:GetRightVec())    end    inst:DoTaskInTime(.4, function() 		local sz = (inst.components.growable and inst.components.growable.stage > 2) and .5 or .25		GetPlayer().components.playercontroller:ShakeCamera(inst, "FULL", 0.25, 0.03, sz, 6)    end)        RemovePhysicsColliders(inst)    inst.AnimState:PushAnimation(inst.anims.stump)		inst:AddComponent("workable")    inst.components.workable:SetWorkAction(ACTIONS.DIG)    inst.components.workable:SetOnFinishCallback(dig_up_stump)    inst.components.workable:SetWorkLeft(1)        inst:AddTag("stump")    if inst.components.growable then        inst.components.growable:StopGrowing()    end    inst:AddTag("NOCLICK")    inst:DoTaskInTime(2, function() inst:RemoveTag("NOCLICK") end)        local days_survived = GetClock().numcycles    if days_survived >= TUNING.LEIF_MIN_DAY then		if math.random() <= TUNING.LEIF_PERCENT_CHANCE then							local numleifs = 1			if days_survived > 30 then				numleifs = math.random(2)			elseif days_survived > 80 then				numleifs = math.random(3)			end						for k = 1,numleifs do								local target = FindEntity(inst, TUNING.LEIF_MAXSPAWNDIST, 					function(item) 						if item.components.growable and item.components.growable.stage <= 3 then							return item:HasTag("tree") and (not item:HasTag("stump")) and (not item:HasTag("burnt")) and not item.noleif						end						return false					end)									if target  then					target.noleif = true					target.leifscale = growth_stages[target.components.growable.stage].leifscale or 1					target:DoTaskInTime(1 + math.random()*3, function()                         if target then    						local target = target    						local leif = SpawnPrefab(builds[target.build].leif)    						local scale = target.leifscale    						local r,g,b,a = target.AnimState:GetMultColour()    						leif.AnimState:SetMultColour(r,g,b,a)    						    						--we should serialize this?    						leif.components.locomotor.walkspeed = leif.components.locomotor.walkspeed*scale    						leif.components.combat.defaultdamage = leif.components.combat.defaultdamage*scale    						leif.components.health.maxhealth = leif.components.health.maxhealth*scale    						leif.components.health.currenthealth = leif.components.health.currenthealth*scale    						leif.components.combat.hitrange = leif.components.combat.hitrange*scale    						leif.components.combat.attackrange = leif.components.combat.attackrange*scale    						    						leif.Transform:SetScale(scale,scale,scale)     						leif.components.combat:SuggestTarget(chopper)    						leif.sg:GoToState("spawn")    						target:Remove()    						    						leif.Transform:SetPosition(target.Transform:GetWorldPosition())                        end					end)				end			end		end    endendlocal function tree_burnt(inst)	OnBurnt(inst)	inst.pineconetask = inst:DoTaskInTime(10,		function()			local pt = Vector3(inst.Transform:GetWorldPosition())			if math.random(0, 1) == 1 then				pt = pt + TheCamera:GetRightVec()			else				pt = pt - TheCamera:GetRightVec()			end			inst.components.lootdropper:DropLoot(pt)			inst.pineconetask = nil		end)endlocal function handler_growfromseed (inst)	inst.components.growable:SetStage(1)	inst.AnimState:PlayAnimation("grow_seed_to_short")    inst.SoundEmitter:PlaySound("dontstarve/forest/treeGrow")          	PushSway(inst)endlocal function onsave(inst, data)    if inst:HasTag("burnt") or inst:HasTag("fire") then        data.burnt = true    end        if inst:HasTag("stump") then        data.stump = true    end	if inst.build ~= "normal" then		data.build = inst.build	endend        local function onload(inst, data)    if data then		if not data.build or builds[data.build] == nil then			inst.build = "normal"		else			inst.build = data.build		end        if data.burnt then            OnBurnt(inst, true)        elseif data.stump then            inst:RemoveComponent("burnable")            MakeSmallBurnable(inst)            inst:RemoveComponent("workable")            inst:RemoveComponent("propagator")            MakeSmallPropagator(inst)            inst:RemoveComponent("growable")            RemovePhysicsColliders(inst)            inst.AnimState:PlayAnimation(inst.anims.stump)            inst:AddTag("stump")                		inst:AddComponent("workable")    	    inst.components.workable:SetWorkAction(ACTIONS.DIG)    	    inst.components.workable:SetOnFinishCallback(dig_up_stump)    	    inst.components.workable:SetWorkLeft(1)        end    endend        local function OnEntitySleep(inst)    inst:RemoveComponent("burnable")    inst:RemoveComponent("propagator")    inst:RemoveComponent("inspectable")endlocal function OnEntityWake(inst)    if not inst:HasTag("burnt") and not inst:HasTag("fire") then        if not inst.components.burnable then            if inst:HasTag("stump") then                MakeSmallBurnable(inst)             else                MakeLargeBurnable(inst)                inst.components.burnable:SetFXLevel(5)                inst.components.burnable:SetOnBurntFn(tree_burnt)            end        end        if not inst.components.propagator then            if inst:HasTag("stump") then                MakeSmallPropagator(inst)            else                MakeLargePropagator(inst)            end        end    end    if not inst.components.inspectable then        inst:AddComponent("inspectable")        inst.components.inspectable.getstatus = inspect_tree    endendlocal function makefn(build, stage, data)	    local function fn(Sim)		local l_stage = stage		if l_stage == 0 then			l_stage = math.random(1,3)		end        local inst = CreateEntity()        local trans = inst.entity:AddTransform()        local anim = inst.entity:AddAnimState()                local sound = inst.entity:AddSoundEmitter()        MakeObstaclePhysics(inst, .25)   		local minimap = inst.entity:AddMiniMapEntity()		if build == "normal" then			minimap:SetIcon("evergreen.png")		elseif build == "sparse" then			minimap:SetIcon("evergreen_lumpy.png")		end		minimap:SetPriority(-1)               inst:AddTag("tree")        inst:AddTag("workable")                inst.build = build        anim:SetBuild(GetBuild(inst).file)        anim:SetBank("evergreen_short")        local color = 0.5 + math.random() * 0.5        anim:SetMultColour(color, color, color, 1)                -------------------                MakeLargeBurnable(inst)        inst.components.burnable:SetFXLevel(5)        inst.components.burnable:SetOnBurntFn(tree_burnt)                MakeLargePropagator(inst)                -------------------                inst:AddComponent("inspectable")        inst.components.inspectable.getstatus = inspect_tree                -------------------        inst:AddComponent("workable")        inst.components.workable:SetWorkAction(ACTIONS.CHOP)        inst.components.workable:SetOnWorkCallback(chop_tree)        inst.components.workable:SetOnFinishCallback(chop_down_tree)                -------------------        inst:AddComponent("lootdropper")                 ---------------------                        ---------------------                inst:AddComponent("growable")        inst.components.growable.stages = growth_stages        inst.components.growable:SetStage(l_stage)        inst.components.growable.loopstages = true        inst.components.growable:StartGrowing()                inst.growfromseed = handler_growfromseed        ---------------------                --PushSway(inst)        inst.AnimState:SetTime(math.random()*2)        ---------------------                     inst.OnSave = onsave         inst.OnLoad = onload        		MakeSnowCovered(inst, .01)        ---------------------        		inst:SetPrefabName( GetBuild(inst).prefab_name )        if data =="burnt"  then            OnBurnt(inst)        end                if data =="stump"  then            inst:RemoveComponent("burnable")            MakeSmallBurnable(inst)                        inst:RemoveComponent("workable")            inst:RemoveComponent("propagator")            MakeSmallPropagator(inst)            inst:RemoveComponent("growable")            RemovePhysicsColliders(inst)            inst.AnimState:PlayAnimation(inst.anims.stump)            inst:AddTag("stump")            inst:AddComponent("workable")            inst.components.workable:SetWorkAction(ACTIONS.DIG)            inst.components.workable:SetOnFinishCallback(dig_up_stump)            inst.components.workable:SetWorkLeft(1)        end        inst.OnEntitySleep = OnEntitySleep        inst.OnEntityWake = OnEntityWake        return inst    end    return fnend    local function tree(name, build, stage, data)    return Prefab("forest/objects/trees/"..name, makefn(build, stage, data), assets, prefabs)endreturn tree("evergreen", "normal", 0),		tree("evergreen_normal", "normal", 2),        tree("evergreen_tall", "normal", 3),        tree("evergreen_short", "normal", 1),		tree("evergreen_sparse", "sparse", 0),		tree("evergreen_sparse_normal", "sparse", 2),        tree("evergreen_sparse_tall", "sparse", 3),        tree("evergreen_sparse_short", "sparse", 1),        tree("evergreen_burnt", "normal", 0, "burnt"),        tree("evergreen_stump", "normal", 0, "stump") 

 

I'd like to somehow make it all work through modmain.lua without overriding any existing files.

But how do I get and set variables from different parts of the game logics?

I've dozens of tabs open in my browser of all the forum pages and stuff but can't find a sensible documentation.

It's always either a specific example that is creating something new or, even worse, art guides.....tons of them...

 

Now how do I get rid of saplings?

 

(Sorry for multipost. The editing function wasn't forking for some reason. Now that it does, I will not multipost anymore.)

Edited by Erquint
Link to comment
https://forums.kleientertainment.com/forums/topic/37251-newbie-questions/
Share on other sites

i think the world gen has to be modified

How?

If You know of any competent documentation — kick a link to it in my general direction.

 

Managed to make the sapling be unpickable for starters:

entity.components.pickable.canbepicked = false

The topology of the fields and functions seems opaque as there is no documentation. The simplest things take ages to figure out blindly by poking around.

How do people make mods for the engine that has no class reference? There should be one Klei released somewhere...

Why do I have to reverse-engineer the game to get this list of AnimState functions for example?

SetLayer

SetRayTestOnBB

SetFinalOffset

SetManualBB

SetOrientation

GetSymbolPosition

Hide

SetSortOrder

ClearOverrideSymbol

SetBloomEffectHandle

SetDepthWriteEnabled

SetDepthTestEnabled

Show

PlayAnimation

SetTime

Pause

Resume

SetDeltaTimeMultiplier

PushAnimation

GetMultColour

OverrideSymbol

SetPercent

SetAddColour

ClearBloomEffectHandle

SetScale

SetBank

PutOnGround

SetBuild

GetCurrentFacing

SetErosionParams

GetAddColour

LoadAnimation

SetLightOverride

SetDepthBias

AnimDone

SetMultColour

This and all the functions, classes, fields and tables should be in the documentation, especially the C++ part which is at the footstone of the whole game yet is closed.

 

How do I make all saplings invisible?

Is there a blank animation I can make it play?

If I nil out the animations straight ahead — the engine cries for an animation to play and crashes.

Tried different things, none seemed to work as I don't even know what are the fields a variable has.

Edited by Erquint

Not much people answered and fiddling with hooks was too much of a hassle to take after 2 days of banging my head against it.
With the current state of documentation which I have already ranted enough about I decided to make it the old gritty way of overriding files.
Made a working RoG mod that way but will look into converting it into a modmain.lua API hook-based mod later.
 
Also why isn't there a way to make a mod that supports both modes by having the mod be split up into 2-in-1 kinda thing?
For example, even when I have the mod API set to 6 and compatible with both modes arranged like this
ModName
  -modinfo.lua

  -DLC001
    -scripts

      -...
  -scripts
    -...
 
The DLC001 part gets completely ignored by game and the mod menu gets freaked out for no reason. (Where There's a Wilson... rev.102572)
 
API 6 also gives a "Crashed" and then "To be disabled" messages without actually crashing and disabling. API 5 works great but shows out-of-date message. I'd like to make it compatible with both vanilla and RoG even if I'd have to include twice the files with one set loading for vanilla and another one for RoG.

 

Will test and hotfix the mod for some time and post it then. Also tried both Matt's Klei tool and Github tools to make modicon.tex but none proved working, whatever then, who needs an icon(sarcasm?).

Edited by Erquint

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