Jump to content

How to reduce "detection" distance?


Recommended Posts

For a custom character I am making, I want to be able to sneak around enemies or be able to get closer to a mob without it knowing I'm there. For example, I could get closer to a rabbit before it notices me and runs back to its home, or I can get closer to a bird before it flies off. I'm aware of the "scarytoprey" and "notarget" tags, but they basically remove detection altogether. Any help would be greatly appreciated!

Edited by Romanful
Link to comment
Share on other sites

 

Update, I had the idea to look in tuning.lua and found some lines that might be helpful. Each mob has a "(mob)_TARGET_DIST =  (#)" line. If if could figure out how to insert this into my mod and modify the values, I might have figured out what I was looking for.

Update, Started looking in brain files, and found what I'm certain I'm looking for; 

local STOP_RUN_DIST = 10
local SEE_PLAYER_DIST = 5

local AVOID_PLAYER_DIST = 3
local AVOID_PLAYER_STOP = 6

is what I found in rabbitbrains.lua

local SEE_THREAT_DIST = 5

local function ShouldFlyAway(inst)
    return not (inst.sg:HasStateTag("sleeping") or
                inst.sg:HasStateTag("busy") or
                inst.sg:HasStateTag("flight"))
        and (TheWorld.state.isnight or
            (inst.components.health ~= nil and inst.components.health.takingfiredamage and not (inst.components.burnable and inst.components.burnable:IsBurning())) or
            FindEntity(inst, SEE_THREAT_DIST, nil, nil, { "notarget", "INLIMBO" }, { "player", "monster", "scarytoprey" }) ~= nil)
end

was found in birdbrains.lua.

So I'm sure if I change these variables in modmain.lua, it would work correctly. The only thing is, I'm not adept with coding right now, so I'm not exactly sure how to do it exactly. I only wish for passive animals that run away when the player gets close not to run away until  half the default player distance. So the player can get closer to rabbits before they run, closer to butterflies, etc. Any help would be greatly appreciated.

Link to comment
Share on other sites

I'm no expert on editing already existing brains, but...

You have to edit the brain - replace the function that determines the distance with your custom one.

Download a mod that edits these things (for example The Tallbird Mod) and look how the author did it.

The best way to do things would be to check entity tag (so you know if it's your character or not). Use old function for other characters and a new function for your custom character.

Edited by PanAzej
Link to comment
Share on other sites

3 hours ago, PanAzej said:

I'm no expert on editing already existing brains, but...

You have to edit the brain - replace the function that determines the distance with your custom one.

Download a mod that edits these things (for example The Tallbird Mod) and look how the author did it.

The best way to do things would be to check entity tag (so you know if it's your character or not). Use old function for other characters and a new function for your custom character.

Alright well I learned from that mod is that it can all be done from modmain.lua! maybe that was really obvious already. I looked through the tallbird mod's modmain and the only references I could find were towards the end:

local function DumpBT(bnode, indent)
	local s = ""
	for i=1,indent do
		s = s.."|   "
	end
	s = s..bnode.name
	print(s)
	if bnode.children then
		for i,childnode in ipairs(bnode.children) do
			DumpBT(childnode, indent+1)
		end
	end
end

AddBrainPostInit("smallbirdbrain", function(brain)

	--print("\n\n\t\t\t\tDumping a smallbirdbrain")
	--DumpBT(brain.bt.root, 0)
	--print("\n\n")

--[[	local function CustomFollow(inst, oldfn)
		print("Confirmed CustomFollow run from main code that edits RunAway")
		return oldfn(inst)
	end
--]]
 
    local function CustomRunAway(inst, target, oldfn)
	--print("Confirmed CustomRunAway run from main code that edits RunAway")
        if target and target:HasTag("player") then
			if not inst.components.follower.leader then
				inst.components.trader:SetAcceptTest(ShouldAcceptItem)
				inst.components.trader.onaccept = OnGetItemFromPlayer
				inst.components.trader.onrefuse = OnRefuseItem
				inst.components.follower:SetLeader(target)
				--target.components.leader:AddFollower(inst)
				--inst:AddComponent("named")
				--inst.components.named.possiblenames = STRINGS.PIGNAMES
				--inst.components.named:PickNewName()
			end
			--print("returning false - do not run away..")
            return false -- Do not run away.
        else
		--print("returning original function")
            return oldfn(inst,target) -- Return the old function to handle it
        end
    end
    
	
    local index = nil
	local index2 = nil
     
    for i,v in ipairs(brain.bt.root.children) do
        if v.name == "RunAway" then
            index = i
            break
        end
    end
    
	for i,v in ipairs(brain.bt.root.children[3].children[2].children[2].children) do
        if v.name == "Follow" then
			index2 = i
			break
		end
    end
	
    if not index then
        --print("RunAway Not Found!")
		return
    end
   
   --print("RunAway Found!")
   local old_shouldrunfn = brain.bt.root.children[index].shouldrunfn
   brain.bt.root.children[index].shouldrunfn = function(target) return CustomRunAway(brain.inst, target, old_shouldrunfn) end
   
   if not index2 then
        --print("Follow Not Found!")
		return
    end
   local old_shouldrunfn2 = brain.bt.root.children[3].children[2].children[2].children[index2].shouldrunfn

end)

I feel like the answer to it all is really simple, like in the end it will only be 3 lines of code. Well, until we can figure it out!

Link to comment
Share on other sites

4 hours ago, SuperDavid said:

Just wanted to say I really hope someone can help you with thiss because I want to do something similar but have no idea how xD...

I hope someone figures it out too. Otherwise I'll probably just do the "remove scarytoprey" thing. I feel like that would be too easy and underwhelming though.

Link to comment
Share on other sites

I think the variables you want are local variables in the brain file. There's no global context that you can change them in to easily switch between on and off. Also, the action definitions are also loaded once when the brain is created, so to change them in the middle of a creature's lifecycle isn't really something you can do either.

In theory you could go to the brain logic on the instance, access the brain, pull out the priority nodes, grab the specific actions you want to alter, and then change their inner variables to change the sight distance. I think that might be fragile, and may not work as new actions are created. 

Someone else may know a way to do it, but from what I know, it's not a matter of changing one variable. Sorry

Link to comment
Share on other sites

3 hours ago, Romanful said:

I feel like the answer to it all is really simple, like in the end it will only be 3 lines of code. Well, until we can figure it out!

It's not this easy, really. You can't change local variables and editing tuning variables would change them for all players.

You can see how this person did it: They used AddBrainPostInit function, in which they created a custom behaviour function, then they checked the original brain and from there they executed their custom function, while also sending the old function in a variable. It's probably the best, most bug-free way of doing it.

Link to comment
Share on other sites

Unfortunately for this method I came up with it's not very mod-friendly due to Klei using a local variable to determine aggro ranges.

I have provided a base template for assisting with adding in different prefabs, but there's also the SpringCombatMod and other nuances for different prefabs not being taken into consideration that will need to be done for a perfect aggro range modifier.

local AGGRO_MULTIPLIER = 0.5
local AGGRO_EDITED_SQ = {}
AGGRO_EDITED_SQ.spider = GLOBAL.TUNING.SPIDER_TARGET_DIST
AGGRO_EDITED_SQ.bat = GLOBAL.TUNING.BAT_TARGET_DIST
AGGRO_EDITED_SQ.lightninggoat = GLOBAL.TUNING.LIGHTNING_GOAT_TARGET_DIST
AGGRO_MULTIPLIER = AGGRO_MULTIPLIER*AGGRO_MULTIPLIER
for k,v in pairs(AGGRO_EDITED_SQ)
do
    AGGRO_EDITED_SQ[k] = AGGRO_MULTIPLIER*v*v
end
AddClassPostConstruct(
    "components/combat_replica",
    function(self)
        local CanTarget_old = self.CanTarget
        self.CanTarget = function(self, target, ...)
            if target and target:IsValid() and target:HasTag("player")
            then
                local radcheck = AGGRO_EDITED_SQ[self.inst.prefab]
                if radcheck and (GLOBAL.distsq(target:GetPosition(), self.inst:GetPosition()) > radcheck)
                then
                    return false
                end
            end
            return CanTarget_old(self, target, ...)
        end
    end
)

I didn't put a global aggro range cap because some prefabs may act very odd when having their ranges edited.

Link to comment
Share on other sites

Welp, CarlZalph put code before me xD, but I'll post my code I came up with anyways :)!

Here it is @Romanful

inst:DoPeriodicTask(0.1, function()
	local CAN_ATTACK =
	{
		CHOP = false,
		DIG = false,
		HAMMER = false,
		MINE = false,
		ATTACK = true,
	}

	local ATTACK_TAGS = {"_combat"}

	for k, v in pairs(CAN_ATTACK) do
		table.insert(ATTACK_TAGS, k.."_workable")
	end

	local NON_ATTACK_TAGS = {"INLIMBO", "playerghost"}

	local x, y, z = inst.Transform:GetWorldPosition()
	local ents = TheSim:FindEntities(x, y, z, 2, nil, NON_ATTACK_TAGS, ATTACK_TAGS)
	
   for i, v in ipairs(ents) do
      if not v:HasTag("player") and not v:HasTag("shadow") and not v:HasTag("shadowminion") and not v:HasTag("shadowchesspiece") and v.components.health and not v.components.health:IsDead() and inst.components.health and not inst.components.health:IsDead() then -- No hiding from nightmare creatures, mwuhahaha!
         inst:RemoveTag("notarget")
	     inst:AddTag("scarytoprey")
	  else
		 inst:AddTag("notarget")
		 inst:RemoveTag("scarytoprey")
      end
   end 
end)

You'd put this inside your character's master_postinit & change the number 2 to whatever you want your detection range to be, pretty simple & straight forward, I think :)!

I dunno if my code would effect performance since it's being called every .1 seconds...though my performance was the same with or without this code, so it probably doesn't effect performance :D!

Edited by SuperDavid
Link to comment
Share on other sites

Thank you @SuperDavid and @CarlZalph for your codes, I'll test them out when I get home! I do understand David's code more than Carl's (aka i don't understand Carl's at all, but that doesn't mean it won't work). David, would this work for all creatures (spiders, rabbits, birds, gobblers, grass geckos, etc)?

But thanks again for putting in the effort of writing it!

Edited by Romanful
Link to comment
Share on other sites

5 hours ago, Romanful said:

David, would this work for all creatures (spiders, rabbits, birds, gobblers, grass geckos, etc)?

But thanks again for putting in the effort of writing it!

It works for all entities except Nighmares, if you want it to work for them too then get rid of

and not v:HasTag("shadow") and not v:HasTag("shadowminion") and not v:HasTag("shadowchesspiece")

from :)

for i, v in ipairs(ents) do
      if not v:HasTag("player") and not v:HasTag("shadow") and not v:HasTag("shadowminion") and not v:HasTag("shadowchesspiece") and v.components.health and not v.components.health:IsDead() and inst.components.health and not inst.components.health:IsDead() then -- No hiding from nightmare creatures, mwuhahaha!
         inst:RemoveTag("notarget")
	     inst:AddTag("scarytoprey")
	  else
		 inst:AddTag("notarget")
		 inst:RemoveTag("scarytoprey")
      end
   end 

 

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