Jump to content

Recommended Posts

I have mychar mod almost ready. He will be able to root and when he is rooted he will heal nearby players.

What is happening: it will also piss off nearby hostiles even when they are not provoked (such as Tree guard wandering around peacefully suddenly angry at mychar)

What I want: only do SuggestTarget() when the hostile is in "attack" (or hunt or combat) mode

Kind of like in mmorpg where the mob switches target to healer.

How do I accomplish this?

 

Current code (modmain)

 

Spoiler

 

--suggest self as target for all nearby hostiles
    local healer = inst
    local x,y,z = healer.Transform:GetWorldPosition()
    local ents = TheSim:FindEntities(x,y,z,healradius) --healradius is a variable
    for i, target in ipairs(ents) do
        if target ~= healer and target.entity:IsVisible() and target.components.health ~= nil and target.components.locomotor ~= nil and not target:HasTag("player") and not target:HasTag("structure") and not target:HasTag("eyeplant") and not target:HasTag("wall") then
            --suggest target
            if target.components.combat then
                target.components.combat:SuggestTarget(healer)
            end
        end
    end

 

 

 

Side question:
I read that FindEntities() is not CPU-friendly. Is there more efficient way?

Edited by SenL
1 hour ago, SenL said:

How do I accomplish this?

            if target.components.combat and target.components.combat.target then
                target.components.combat:SuggestTarget(healer)
            end

 

1 hour ago, SenL said:

I read that FindEntities() is not CPU-friendly. Is there more efficient way?

In this case, I don't think so.

But your character isn't permanently rooted, so... it's ok.

1)

You added

target.components.combat.target

Does it mean that the target has a "target" (meaning when target is not provoked this "target" is nil)?

 

2)

Can I call the heal task every second? (currently every 10 seconds)

Will it be bad for CPU?

Yes, it has

inst:GetDistanceSqToInst() for each GLOBAL.AllPlayers

and then it has

TheSim:FindEntities(x,y,z, healradius) --healradius is a variable

 

I could split them up into two tasks... one heal every second, and the findentities every 5 or 10... not sure

Edited by SenL
Just now, SenL said:

Yes, it has

inst:GetDistanceSqToInst() for each GLOBAL.AllPlayers

and then it has

TheSim:FindEntities(x,y,z, healradius) --healradius is a variable

Post the full function, because I am thinking there is actually a better way of optimizing it so you don't burn cpu cycles iterating through useless junk.

Sure.

local function dorootedhealtask(inst)
    local healradius = 70 --may be too high?
    
    --heal all non-ghost players nearby
    for _, v in pairs(GLOBAL.AllPlayers) do
        if not v:HasTag("playerghost") then
            if inst:GetDistanceSqToInst(v) < healradius then
                if v.components.sanity then
                    v.components.sanity:DoDelta(10)
                end
                
                if v.components.health then
                    v.components.health:DoDelta(10)
                end
            end
        end
    end
    
    --suggest self as target for all nearby hostiles
    local healer = inst
    local x,y,z = healer.Transform:GetWorldPosition()
    local ents = TheSim:FindEntities(x,y,z,healradius)
    for i, target in ipairs(ents) do
        if target ~= healer and target.entity:IsVisible() and target.components.health ~= nil and target.components.locomotor ~= nil and not target:HasTag("player") and not target:HasTag("structure") and not target:HasTag("eyeplant") and not target:HasTag("wall") then
            --suggest target
            if target.components.combat and target.components.combat.target then
                target.components.combat:SuggestTarget(healer)

            end
        end
    end
end

5 minutes ago, SenL said:

Sure.

There is definitely an easier and more efficient way. You're currently running 2 loops, when in fact you could only run a single loop.

 local function dorootedhealtask(inst)
    local healradius = 70 --may be too high?
    
    --suggest self as target for all nearby hostiles
    local healer = inst
    local x,y,z = healer.Transform:GetWorldPosition()
    local ents = TheSim:FindEntities(x,y,z,healradius)
    for i, target in ipairs(ents) do
		if target ~= healer and target.entity:IsVisible() and target.components.health ~= nil and target.components.locomotor ~= nil and not target:HasTag("player") and not target:HasTag("structure") and not target:HasTag("eyeplant") and not target:HasTag("wall") then
		--suggest target
			if target.components.combat and target.components.combat.target then
				target.components.combat:SuggestTarget(healer)
			end
		else
			if not target:HasTag("playerghost") and target:HasTag("player") then
    	        if inst:GetDistanceSqToInst(target) < healradius then
	                if target.components.sanity then
            	        target.components.sanity:DoDelta(10)
        	        end
                
    	            if target.components.health then
	                    target.components.health:DoDelta(10)
                	end
            	end
        	end
        end
    end
end 

This single loop now iterates through all the entities, healing players when it comes across them and forcing targets to attack the healer.

Edited by Kzisor
Fixed code errors, changed v: to traget:
8 minutes ago, SenL said:

Does it mean that the target has a "target" (meaning when target is not provoked this "target" is nil)?

Yeah, when mobs have a target on their combat component, they will enter the ChaseAndAttack behavior sooner or later.

So if a mob has a combat target, then it's angry.

Is there a difference between

local ents = TheSim:FindEntities(x,y,z,healradius)
for i, target in ipairs(ents) do
    if target ~= healer and target.entity:IsVisible() and target.components.health ~= nil and target.components.locomotor ~= nil and not target:HasTag("player") and not target:HasTag("structure") and not target:HasTag("eyeplant") and not target:HasTag("wall") then

and

local ents = TheSim:FindEntities(x,y,z,healradius, nil, {"structure", "eyeplant", "wall"})
for i, target in ipairs(ents) do
    if target ~= healer and target.entity:IsVisible() and target.components.health ~= nil and target.components.locomotor ~= nil and not target:HasTag("player") then

performance-wise?

1 hour ago, Muche said:

Is there a difference between


local ents = TheSim:FindEntities(x,y,z,healradius)
for i, target in ipairs(ents) do
    if target ~= healer and target.entity:IsVisible() and target.components.health ~= nil and target.components.locomotor ~= nil and not target:HasTag("player") and not target:HasTag("structure") and not target:HasTag("eyeplant") and not target:HasTag("wall") then

and


local ents = TheSim:FindEntities(x,y,z,healradius, nil, {"structure", "eyeplant", "wall"})
for i, target in ipairs(ents) do
    if target ~= healer and target.entity:IsVisible() and target.components.health ~= nil and target.components.locomotor ~= nil and not target:HasTag("player") then

performance-wise?

The way I understand TheSim:FindEntities working is that we're querying the C/C++ engine of Don't Starve asking it to return us entities within the radius. Passing the tags to C/C++ versus keeping the checks in LUA is a very minor performance issue. While yes it would be faster to do it inside C/C++ you gain overhead for passing those parameters which reduce the true benefit gained from it. All in all it is about the same performance wise.

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