Jump to content

Creatures that follow an inventory item will not update their follow target when the item ownership changes


hoxi
  • Fixed

This can cause creatures following you (or a container carrying the item) to keep following you (or the container) even after dropping the item or putting it somewhere else, which can lead to them to walk into danger when you intended to leave them somewhere safe. It can be pretty inconvenient to need to wait for them to catch up, especially if something else is chasing you.

This used to only be the case with Beefalos, but now it applies to other creatures like Chester, Hutch, Glommer, etc. It got introduced with the new features of making item leaders forward leadership to the item owner (this is all fine, this is really more of an intrinsic issue with the Follow behavior, see below).

 

The core of the issue is, dropping or moving the item to a container doesn't update the Follow behavior's target, so they'll keep moving until they get close, and only then will follow the new owner if applicable, or the item itself if dropped.

 

This could be solved in a few different ways, like by tweaking the behavior's logic a bit (this could prevent any future issues, and would solve it for Beefalos as well):

Spoiler
function Follow:Visit()
    --cached in case we need to use this multiple times
    local dist_sq, target_pos

    if self.status == READY then
		local prev_target = self.currenttarget
        self.currenttarget = self:GetTarget()
        if self.currenttarget ~= nil then
            dist_sq, target_pos = _distsq(self.inst, self.currenttarget)

			if prev_target ~= self.currenttarget or self.alwayseval then
				self:EvaluateDistances()
			end

            local on_different_platforms = self:AreDifferentPlatforms(self.inst, self.currenttarget)

            if not on_different_platforms and dist_sq < self.min_dist * self.min_dist then
                self.status = RUNNING
                self.action = "BACKOFF"
            elseif on_different_platforms or dist_sq > self.max_dist * self.max_dist then
                self.status = RUNNING
                self.action = "APPROACH"
            else
                self.status = FAILED
            end
        else
            self.status = FAILED
        end
    end

    if self.status == RUNNING then
        if self.currenttarget == nil
            or not self.currenttarget:IsValid()
            or (self.currenttarget.components.health ~= nil and self.currenttarget.components.health:IsDead())
            or (self.inlimbo_invalid and self.currenttarget:IsInLimbo())
        then
            self.status = FAILED
            self.inst.components.locomotor:Stop()
            return
        end

        if self.action == "APPROACH" then
            if dist_sq == nil then
                dist_sq, target_pos = _distsq(self.inst, self.currenttarget)
            end

            local different_platforms = self:AreDifferentPlatforms(self.inst, self.currenttarget)

            if not different_platforms and dist_sq < self.target_dist * self.target_dist then
                self.status = SUCCESS
                return
            end

            local max_dist = self.max_dist * .75

            if different_platforms then
                max_dist = 0
            end
            if self.canrun and (dist_sq > max_dist * max_dist or self.inst.sg:HasStateTag("running")) then
                self.inst.components.locomotor:GoToPoint(target_pos, nil, true)
            else
                self.inst.components.locomotor:GoToPoint(target_pos)
            end
        elseif self.action == "BACKOFF" then
            if dist_sq == nil then
                dist_sq, target_pos = _distsq(self.inst, self.currenttarget)
            end
            if dist_sq > self.target_dist * self.target_dist then
                self.status = SUCCESS
                return
            end
            local angle = self.inst:GetAngleToPoint(target_pos)
            if self.canrun then
                self.inst.components.locomotor:RunInDirection(angle + 180)
            else
                self.inst.components.locomotor:WalkInDirection(angle + 180)
            end
        end

        self:Sleep(.25)
    end
end

It currently only updates the current target when status is READY, not during RUNNING. Checking during RUNNING in some way or another, would help.

Could do something similar to FaceEntity, or simply add the functionality to be intrinsic to the behavior, without needing to provide an additional function. The code inside the if self.status == READY then block shouldn't necessarily be restricted to itself, it could run when:

  • self.status == READY
  • self.currenttarget ~= self:GetTarget()

 

Just throwing some suggestions anyhow, but I do believe that solving it through the behavior itself one way or another would be a nice way to future-proof this from happening again, and with otherwise minimal impact.

 

Oh, and one last thing, while the Approach behavior is unused, it's otherwise very similar to Follow, and would suffer the same issue so it'd need to be updated too (maybe some of the code could be made as part of a common file to share with both?).


Steps to Reproduce
  • Get Chester's Eyebone.
  • Move away from Chester, so that he's a good few tiles of distance away.
  • Drop the Eyebone (or put it in a container) while Chester is trying to catch up to you, then keep moving to keep distance from Chester.
  • Notice how he will keep following you instead of the Eyebone.
  • Stop until he catches up.
  • Notice how he will now actually try to get back to the dropped Eyebone.
  • Like 2



User Feedback


A developer has marked this issue as fixed. This means that the issue has been addressed in the current development build and will likely be in the next update.


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