How does Spring.SetSquareBuildingMask() work?

How does Spring.SetSquareBuildingMask() work?

Discuss Lua based Spring scripts (LuaUI widgets, mission scripts, gaia scripts, mod-rules scripts, scripted keybindings, etc...)

Moderator: Moderators

Post Reply
User avatar
Forboding Angel
Evolution RTS Developer
Posts: 14673
Joined: 17 Nov 2005, 02:43

How does Spring.SetSquareBuildingMask() work?

Post by Forboding Angel »

I know there is also a unitdef tag called buildMask and I assume it works as a bitfield like shields do.

However, in my attempts to use it, I am either met with incorrect usage errors or nothing seems to happen.

The place where I am trying to use it is here:
https://github.com/Spring-Helper-Projec ... ictory.lua

Specifically via Spring.SetSquareBuildingMask(capturePoint.x, capturePoint.z, captureRadius)

So an example return for those arguments is: 3744, 838, 500

However, that sparked off this issue:

Code: Select all

[f=0000030] Error: LuaRules::RunCallIn: error = 2, GameFrame, [Internal Lua error: Call failure] [string "LuaRules/Gadgets/game_controlvictory.lua"]:687: Invalid values supplied: SetSquareBuildingMask(3744, 838, 500)
stack traceback:
	[C]: in function 'SetSquareBuildingMask'
	[string "LuaRules/Gadgets/game_controlvictory.lua"]:687: in function 'GameFrame'
	[string "LuaRules/gadgets.lua"]:879: in function <[string "LuaRules/gadgets.lua"]:877>
	(tail call): ?
But interestingly only 2 of the 7 points returned an error. At any rate, so some number jockeying later, I had it loaded without errors, however, when in the placement stage of building, there was no visible mask over the control points. I had not added anything to the unitdefs, but as I understand it, I shouldn't have to, but the thing that gets me even more is that there doesn't seem to be a bitfield value that gets applied to the call, as a result, how is that supposed to work? Does it just default to a bitfield value of 1?

Image


Additionally, is there any way to make the mask rounded instead of a flat square?
Kloot
Spring Developer
Posts: 1867
Joined: 08 Oct 2006, 16:58

Re: How does Spring.SetSquareBuildingMask() work?

Post by Kloot »

1) the x and z arguments must be converted to "half heightmap" coordinates, divide both by (8 * 2)
2) the third argument is not a radius but a 16-bit mask, meaning construction is blocked on the square (x,z) for all buildings for which (squareMask & unitdefMask) != unitdefMask
3) the default buildingMask value is 1; Spring.SetSquareMask(x, z, 500) will by default cause any construction at (x,z) to be blocked since (500 & 1) != 1
User avatar
FLOZi
MC: Legacy & Spring 1944 Developer
Posts: 6240
Joined: 29 Apr 2005, 01:14

Re: How does Spring.SetSquareBuildingMask() work?

Post by FLOZi »

Example from MCL:

https://github.com/SpringMCLegacy/Sprin ... er.lua#L63

Code: Select all

function BuildMaskCircle(cx, cz, r, mask)
local r2 = r * r
local step = Game.squareSize * 2
for z = 0, 2 * r, step do -- top to bottom diameter
local lineLength = math.sqrt(r2 - (r - z) ^ 2)
for x = -lineLength, lineLength, step do
local squareX, squareZ = (cx + x)/step, (cz + z - r)/step
if squareX > 0 and squareZ > 0 and squareX < Game.mapSizeX/step and squareZ < Game.mapSizeZ/step then
Spring.SetSquareBuildingMask(squareX, squareZ, mask)
--Spring.MarkerAddPoint((cx + x), 0, (cz + z - r))
end
end
end
end
I use this to make it so that turrets can be built/deployed only within a certain radius of the beacon which has the turret control tower.
User avatar
Forboding Angel
Evolution RTS Developer
Posts: 14673
Joined: 17 Nov 2005, 02:43

Re: How does Spring.SetSquareBuildingMask() work?

Post by Forboding Angel »

Thanks guys :-)

Ok, so Flozi's function looks like it does exactly what I need, but I'm not sure how to interpret it to my uses.

For one thing, so the mask only covers that one single point? Or does sizex/y divided by (8*2) essentially make it cover a 1x1 footprint (assuming my math in my head is right this would be correct).

But I don't see where you define how large the mask is. Are you just giving a radius and checking every single point in the radius? Isn't that super inefficient?
sprunk
Posts: 100
Joined: 29 Jun 2015, 07:36

Re: How does Spring.SetSquareBuildingMask() work?

Post by sprunk »

For one thing, so the mask only covers that one single point? Or does sizex/y divided by (8*2) essentially make it cover a 1x1 footprint (assuming my math in my head is right this would be correct).
The callout itself sets the mask of a 0.5 x 0.5 footprint (note that when you build a 1x1 building it is composed of four even tinier squares).

Flozi's wrapper handles a circle

Code: Select all

function BuildMaskCircle(cx, cz, r, mask)
centered at (cx, cz) of radius r.
Are you just giving a radius and checking every single point in the radius? Isn't that super inefficient?
It's not inefficient. It's the optimal way under current API and it's only done once anyway.
User avatar
Forboding Angel
Evolution RTS Developer
Posts: 14673
Joined: 17 Nov 2005, 02:43

Re: How does Spring.SetSquareBuildingMask() work?

Post by Forboding Angel »

I have utterly failed to do this on my own, can someone give me a hand?
ivand
Posts: 310
Joined: 27 Jun 2007, 17:05

Re: How does Spring.SetSquareBuildingMask() work?

Post by ivand »

Firstly, the only use case here is to block construction of certain buildings in selected locations.

Secondly, the masks need to be specified in slopeMap resolution, basically regular coordinates / 16. This is a natural resolution buildings snap to.

Thirdly, as mentioned the building is allowed in the location if (tile's Mask AND unitDef' buildingMask) == unitDef' buildingMask. buildingMask is 16 bit wide integer.

Here is the practical example:
Usually though, you don't want certain buildings in selected places, e.g. regular buildings on mex and geo spots and other way around. So we will use three bit long values and masks.

Regular terrain will have 001 in BIN, geo spot and area around - 010 BIN, mex and area around - 100 BIN. You place those values on the map using Spring.SetSquareBuildingMask call. Don't forget to convert BINary values to DECimal.

Next in unitDef files you set your geo's buildingMask to 010 (2 DEC) and your mex's buildingMask to 100 (4 DEC). As buildingMask default value is 1, you don't need to touch other unitDefs.

Clearly if tile's and unitdef's masks match then unit is allowed for construction. Here a few examples on when masks don't match.
  • Regular terrain tile 001 AND mex's buildingMask 100 = 000 <-- construction is not allowed
  • Geo terrain tile 010 AND regular building's buildingMask 001 = 000 <-- construction is not allowed
I'd suggest that you start with something really simple e.g.

Code: Select all

Spring.SetSquareBuildingMask(0, 0, 2)
Spring.SetSquareBuildingMask(0, 1, 2)
Spring.SetSquareBuildingMask(1, 0, 2)
Spring.SetSquareBuildingMask(1, 1, 2)
and then set some 2x2 footprint unitdef's buildingMask to 2 as well and hover a building's blueprint around (0,0) corner of the map and see how it pans out.
User avatar
Forboding Angel
Evolution RTS Developer
Posts: 14673
Joined: 17 Nov 2005, 02:43

Re: How does Spring.SetSquareBuildingMask() work?

Post by Forboding Angel »

I feel like you didn't really read the OP.

I know how it works (Kloot answered that very nicely), however, getting to to work in conjunction with CV is the issue. Should be simple, but it is proving to be difficult for me.

Edit: That said, your post gave me a way to visualize it a bit better. Trying again.
User avatar
Forboding Angel
Evolution RTS Developer
Posts: 14673
Joined: 17 Nov 2005, 02:43

Re: How does Spring.SetSquareBuildingMask() work?

Post by Forboding Angel »

Ok, so I made some progress. Instead of working with flozi's function right off the bat, I decided to try and get it to work with a single 1x1 square.

This is what I now have:
Image

Using this:

Code: Select all

		-- Set building Masks for control points
		for _, capturePoint in pairs(points) do
			Spring.SetSquareBuildingMask(capturePoint.x * 0.0625, capturePoint.z * 0.0625, 2)
			Spring.Echo(capturePoint.x * 0.0625, capturePoint.z * 0.0625)
		end
It took me a bit to realize that Kloot meant /16 for half heightmap size. From just looking at it I read / 4 (Because if you don't do 8 * 2 as (8*2) then you end up with 1/4).

Anyway, so now I'm going to try to use Flozi's function to expand this further.
User avatar
Forboding Angel
Evolution RTS Developer
Posts: 14673
Joined: 17 Nov 2005, 02:43

Re: How does Spring.SetSquareBuildingMask() work?

Post by Forboding Angel »

Success achieved:

Image

Thanks to flozi for the fantastic radius function (Just modified very slightly to work for my purposes):

Code: Select all

		-- Set building masks for control points
		for _, capturePoint in pairs(points) do
			local r = captureRadius
			local mask = buildingMask
			local r2 = r * r
			local step = Game.squareSize * 2
			for z = 0, 2 * r, step do -- top to bottom diameter
				local lineLength = math.sqrt(r2 - (r - z) ^ 2)
				for x = -lineLength, lineLength, step do
					local squareX, squareZ = (capturePoint.x + x)/step, (capturePoint.z + z - r)/step
					if squareX > 0 and squareZ > 0 and squareX < Game.mapSizeX/step and squareZ < Game.mapSizeZ/step then
						Spring.SetSquareBuildingMask(squareX, squareZ, mask)
						--Spring.MarkerAddPoint((cx + x), 0, (cz + z - r))
					end
				end
			end
		end
Also thanks to ivand for causing me to visualize it differently so that I would be inspired to try again.
Post Reply

Return to “Lua Scripts”