Order-independent Building Placement Collision


Recommended Posts

So currently when you go to place a structure, or plant some crops, the thing that you're planting has a radius that it checks for things in the way. A crockpot has a radius of 3.2 (a bit under one tile), while an icebox has a radius of 1.5. So if you place two crockpots first, at distance of 3.2, you can place an icebox directly between them, because it's still 1.5 from each of the pots. However, if you place the icebox first, each crockpot has to be 3.2 away from the iceboxes.

 

It would be really nice if you didn't have to put things down in order like this. It makes base building really finnicky-- chests can't be put down first, they have to be put down later.

 

If structures/objects stored a blocking radius (e.g. 1.5 for the icebox, 3.2 for the crockpot, etc, maybe defaulting to 1 or whatever), then when placing it would use the smaller of the two radii in each pair to determine placement, that would allow all the configurations that you can currently produce, but not require you to place them in certain orders.

 

In terms of how this could be implemented, there is min_spacing in the recipe table, so most spacings could be acquired that way (math.min(placer.min_spacing, AllRecipes[inst.prefab].min_spacing)). The FindEntities call wouldn't need to be adjusted because it only uses the minimum of the two spacings, so for objects outside of the currently-being-placed-object's min_spacing, the current object's min_spacing is smaller.

Link to comment
Share on other sites

@rezecib, my contribution:

local AllRecipes = GLOBAL.AllRecipesAddPrefabPostInit("world", function(inst)local Map = GLOBAL.getmetatable(inst.Map).__indexlocal DEPLOY_IGNORE_TAGS = { "NOBLOCK", "player", "FX", "INLIMBO", "DECOR" }-- Things that need extra spacelocal PADDING = {	pond = 2,}local pad_spacing = 0for k, v in pairs(PADDING) do	if v > pad_spacing then		pad_spacing = v	endendlocal ITEMOBJ_DEFAULT = 2function Map:CanDeployRecipeAtPoint(pt, recipe)	if not self:IsPassableAtPoint(pt:Get()) then		return false	end	local min_spacing = recipe.min_spacing or 3.2	local ents = TheSim:FindEntities(pt.x, pt.y, pt.z, min_spacing + pad_spacing, nil, DEPLOY_IGNORE_TAGS)	for k, v in pairs(ents) do		if v.entity:IsValid() and 			v.entity:IsVisible() and 			v.components.placer == nil and 			v.entity:GetParent() == nil then			local v_spacing = 0			-- We either have padding or spacing			if PADDING[v.prefab] then				-- If padding, our build must stay away from it				v_spacing = math.max(min_spacing, PADDING[v.prefab])			else				-- If spacing, at least one build remains ungobbled by the other's spacing				-- We give a bit of space to not build over items and trees, boulders, etc.				-- Maybe ITEMOBJ_DEFAULT should be min_spacing? Constant is better though				local v_rec_spacing = AllRecipes[v.prefab] and AllRecipes[v.prefab].min_spacing or ITEMOBJ_DEFAULT				v_spacing = math.min(min_spacing, v_rec_spacing)			end			if v:GetDistanceSqToPoint(pt:Get()) < v_spacing * v_spacing then				return false			end		end	end	return true	endend)
Link to comment
Share on other sites

Hmm, well, might as well keep the weird padding system in place for ponds and chests (although notably it's not catching the swamp or cave versions of ponds, pond_mos and pond_cave). Walls are pretty easy to patch, but it looks like plantables are pretty difficult, because their deployspacing is stored in their dug_prefab instead of the world prefab. So... either have to build a lookup table for them or revamp the way that the item-vs-plant division is handled. Both of those sound pretty bad, but the lookup table is probably easier (but it will be vulnerable to getting out of sync with other code...).

local AllRecipes = GLOBAL.AllRecipes AddPrefabPostInit("world", function(inst) local Map = GLOBAL.getmetatable(inst.Map).__index local DEPLOY_IGNORE_TAGS = { "NOBLOCK", "player", "FX", "INLIMBO", "DECOR" } --Ported from legacy "stupid finalling hack because it's too late to change stuff"--V2C: is it because we rly should be using Physics radius, but--     not everything has Physics defined? OH WELLlocal RECIPE_PADDING ={    treasurechest =    {        pond = 1, --1 more spacing when building treasurchest near pond    },}function Map:CanDeployRecipeAtPoint(pt, recipe)    if not self:IsPassableAtPoint(pt:Get()) then        return false    end    local min_spacing = recipe.min_spacing or 3.2     local padding = RECIPE_PADDING[recipe.name]    local pad_spacing = 0    if padding ~= nil then        for k, v in pairs(padding) do            if v > pad_spacing then                pad_spacing = v            end        end    end    local ents = TheSim:FindEntities(pt.x, pt.y, pt.z, min_spacing + pad_spacing, nil, DEPLOY_IGNORE_TAGS)    for k, v in pairs(ents) do        if v.entity:IsValid() and            v.entity:IsVisible() and            v.components.placer == nil and            v.entity:GetParent() == nil then            local recipe_spacing = min_spacing + (pad_spacing > 0 and padding[v.prefab] or 0)			local v_spacing = AllRecipes[v.prefab] and AllRecipes[v.prefab].min_spacing									or (v:HasTag("wall") and 1 or (3.2))									-- That last case (3.2) is a problem. How do we handle plants?									-- Their spacings are stored in their dug_prefabs, pinecones, etc,									-- and aren't passed on to their "world" prefabs			local spacing = math.min(recipe_spacing, v_spacing)            if v:GetDistanceSqToPoint(pt:Get()) < spacing * spacing then                return false            end        end    end    return true    end end)
Link to comment
Share on other sites

Archived

This topic is now archived and is closed to further replies.

Please be aware that the content of this thread may be outdated and no longer applicable.