Jump to content

Need help merging find entity thing


Recommended Posts

Hello, I need some help optimizing some of my friend's code which I can't figure out how.. Basically I want to merge the "cave" and "wmwa_totem" range checks to reduce any potential lag it could cause on weaker pcs.

Here's the code.

Spoiler

local WMWA_DIG = G.Action()
WMWA_DIG.str = "Wmwa Dig"
WMWA_DIG.id = "WMWA_DIG"
WMWA_DIG.fn = function(act)
	local inst  = act.target
	
	if inst.components.health:IsDead() or inst:HasTag("playerghost") or inst.components.rider:IsRiding() or inst.sg:HasStateTag("busy") or inst.components.locomotor:WantsToMoveForward() then return end
	
	if inst.dig_cd == true then inst.components.talker:Say(GetString(inst, "DIG_COOLDOWN")) return end
	
	--local x, y, z = inst.Transform:GetWorldPosition()
	--print("FIND", inst, radius, musttags and #musttags or 0, canttags and #canttags or 0, mustoneoftags and #mustoneoftags or 0)
	--local cave = G.TheSim:FindEntities(x, y, z, 500, 0, 0, {"cavee", "wmwa_totem"})
	
	local cave = G.GetClosestInstWithTag({"cavee"}, inst, 250)
	local wmwa_totem = G.GetClosestInstWithTag({"wmwa_totem"}, inst, 750) -- Would like to merge the 250 range check with this one
	
	local function WmwaDiggy(inst)
		inst.dig_cd = true
		inst:DoTaskInTime(2, function() inst.dig_cd = nil end)--cooldown 1 minute
		inst.components.locomotor:Stop()
		inst.components.health:SetInvincible(true)
		ShakeAllCameras(CAMERASHAKE.VERTICAL,3, .3, .3, inst, .01)
		if inst.components.playercontroller ~= nil then inst.components.playercontroller:Enable(false) end
		inst.sg:GoToState("wmwa_dig")
		inst.components.talker:Say(GetString(inst, "DIG"))
		inst:DoTaskInTime(.5, function() inst:SetCameraDistance(8) end)
		inst:DoTaskInTime(1, function() inst:SetCameraDistance(5) end)
		inst:DoTaskInTime(2, function() inst:SetCameraDistance(2) end)
		if inst.blind == nil then inst:ScreenFade(false, 3.25) end
	end
	
	if wmwa_totem then
		WmwaDiggy(inst)
		inst:DoTaskInTime(2.75, function() if inst.blind == nil then inst:ScreenFade(true, 1) end inst:SetCameraDistance() local x, y, z = wmwa_totem.Transform:GetWorldPosition() inst.Transform:SetPosition(x+1.5, y, z) if inst.components.playercontroller ~= nil then inst.components.playercontroller:Enable(true) end inst.components.health:SetInvincible(false) end)
		return
	elseif cave and not wmwa_totem then
		WmwaDiggy(inst)
		inst:DoTaskInTime(2.75, function() if inst.blind == nil then inst:ScreenFade(true, 1) end inst:SetCameraDistance() local x, y, z = cave.Transform:GetWorldPosition() inst.Transform:SetPosition(x+1.5, y, z) if inst.components.playercontroller ~= nil then inst.components.playercontroller:Enable(true) end inst.components.health:SetInvincible(false) end)
		return
	else
		inst.components.talker:Say(GetString(inst, "DIG_NO_CAVE"))
	end
end
AddAction(WMWA_DIG)

 

What my friend wants to do is when this action is preformed if there are no "wmwa_totem" in range then this action takes you to the nearest cave_entrance/cave_exit, but if there's a wmwa_totem in the 750 range then it prioritizes taking you to there instead of cave_entrance/cave_exit. The code right now works, but my friend really wants to merge the range checks then unnecessary lag isn't caused since the action already is performance heavy with a 750 range check..

I tried using "FindEntity" function from simutil, but that seems to only get 1 entity which is closest to you and doesn't find all entities within range allowing a specific one priority. I also tried using "TheSim:FindEntities" but it always causes crashes or just doesn't work and I can't figure out why :(..

Hopefully someone smarter than me can help out with this, thanks for your time have a good day/night :D!

Edited by Warbucks
Link to comment
Share on other sites

Look at what GetClosestInstWithTag actually does.

function GetClosestInstWithTag(tag, inst, radius)
    local x, y, z = inst.Transform:GetWorldPosition()
    local ents = TheSim:FindEntities(x, y, z, radius, type(tag) == "string" and { tag } or tag)
    return ents[1] ~= inst and ents[1] or ents[2]
end

It's just a FindEntities call with a tag as a string or a list of tags, and then if the first (closest) entity is not the given inst, return that, otherwise return the second (second closest).

This is how FindEntities works

-- Description of important function, which finds specific entities within a range:
-- TheSim:FindEntities(x, y, z, radius, mustHaveTags, cantHaveTags, mustHaveOneOfTheseTags)

-- We have limited it to entities with the "player" tag, that are also not ghosts or otherwise in limbo.
-- Radius has been set to 10
local players = TheSim:FindEntities(x, y, z, 10, {"player"}, {"playerghost", "INLIMBO"}, nil)

Just leave mustHaveTags and cantHaveTags set to nil, and put in {"cavee", "wmwa_totem"} as mustHaveOneOfTheseTags.

local x, y, z = inst.Transform:GetWorldPosition()
local cavesAndTotems = G.TheSim:FindEntities(x, y, z, 750, nil, nil, {"cavee", "wmwa_totem"})

Then you can run through the cavesAndTotems list and do your checks. The list will be ordered with the closest entity first. The problem is, you want to check two different distances depending on which entity it is. You should basically say "if I have found a cave, and that cave is within a distance of 250 of my x,y,z then choose the cave, and otherwise pick the first totem I saw". All the functions for distance checks use distance squared in order to avoid doing squareroot calls, so when checking the distance you need to do:

(distance squared < range * range)

Here's the rest of the code:

local entity_to_go_to = nil
local cave_within_range = nil
local stopCaveSearch = false

for i, entity_in_list in ipairs(cavesAndTotems) do
	if entity_in_list:HasTag("cavee") then
		if not stopCaveSearch then
			local distance_to_cave = entity_in_list:GetDistanceSqToPoint(x, y, z))
			if (distance_to_cave < (250*250) then
				-- Found a cave within 250 range. 
				cave_within_range = entity_in_list
			end
			stopCaveSearch = true
		end
	else
		-- The entity is a totem.
		entity_to_go_to = entity_in_list
		break
	end
end

-- If we didn't find a single totem, set entity_to_go_to to cave_within_range
if entity_to_go_to == nil then
	entity_to_go_to = cave_within_range
end

-- Remember, at this point entity_to_go_to may still be nil, since we might not have found anything.

 

We always stop searching for caves the second we see one, because we want the closest one and only if it is within range. If the first one we find is outside the range, then we don't want to waste time looking at more caves. If the first cave IS within range, then we don't care about any of the other caves.

However, if we find a totem, we instantly break from the loop. If we do not find a totem at all, we apply the value of the cave_within_range variable to the entity_to_go_to variable. If we didn't find a cave, entity_to_go_to will still be nil, so we did not find any entity to teleport to.

750 is still a large range, but as long as it's not a check being done in a loop or all the time, you should be fine.

Edited by Ultroman
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...