Skip to content

CustomMaps Roofs

wezzzyrek1 edited this page Sep 18, 2026 · 1 revision

Fading roofs

A player who walks into a building disappears under its roof. There are two ways to open the roof while they are inside - the client's own behaviour 8 in MapObjectAnimationInfo, and a map script - and one map can use both.

Behaviour 8 Map script
Lives in MapObjectAnimationInfo<n>.txt inside Lang.mpr Maps\<map>.lua
Fades when the object stands between the camera and the player the player stands in a box around the roof, the building or a rectangle
Applies to every object of the type in that world every object of a type, or only those in a rectangle
A roof built from several pieces each piece on its own box opens as one, with whole
Whatever else hangs over the player only with rows of its own opens with it, with upper
After a change repack Lang.mpr, restart the client re-enter the map

Behaviour 8

One row per object type, in the world's MapObjectAnimationInfo table. From MapObjectAnimationInfo146.txt:

17	0	0.16	8	300	250	250	700	0	0	0	7
13	0	0.16	8	300	610	100	345	0	0	150	7

Object 17 fades inside a box 250 x 250 x 700 at its origin, 0.07 of the way per frame. Object 13's box is 610 x 100 x 345 and lifted 150 above its origin. Every column is explained under Behaviour 8.

A custom map reads its own table by world number like any other world, so a row like these works there unchanged, with your own object type.

From a map script

Helpers has three fades. All three are called from Map.OnObject every frame, and each brings the object back by itself once the player leaves.

By object type

For a model that is a roof wherever it stands. No coordinates: every object of the type opens while the player is under it.

local ROOFS =
{
	[94] = { sizeX = 150, sizeY = 150, whole = true },
}

for iType in pairs(ROOFS) do
	Map.RegisterObject(iType)
end

Map.OnObject(function(o)
	local roof = ROOFS[o.type]

	if roof then
		Helpers.FadeUnder(o, roof)
	end
end)
Key Default Meaning
sizeX sizeY 350 how far the box reaches out, in world units - one tile is 100
below 700 how far under the roof the player still counts
above 60 how far above it - small, so someone standing on the roof keeps it
alpha 0.1 what the roof fades to
rate 0.12 the fraction of the way it moves each frame
whole off open every piece of a building together
link 300 with whole: how far apart two pieces may stand and still be one building
upper off with whole: also open everything in the building that hangs over the player
clear 100 with upper: how far above the player's feet an object has to stand to open
early 0 open this much sooner than sizeX / sizeY say - for pieces standing out in front of the roof
per none with whole: values for one building only - see One building differently

A roof stands well above the floor the player walks - in Lost Land of Tarkan the temple roofs sit at 440 over a floor at 200 - so the box reaches down from the roof. That is what below is for, and why a small below never opens anything.

Whole buildings

Without whole every piece opens on its own box, so a roof made of four slabs opens only over the slabs the player is under. With whole the pieces are joined into buildings as they come into view - two pieces of the type no more than link apart along each axis are the same building - and all of them open together. sizeX and sizeY then reach out from the building's outermost pieces, so they are the distance from those pieces to the walls.

Everything over the player

A roof is often only one layer of what hangs over the player. The pyramid in Lost Land of Tarkan has a tier of walls at 400 under its roof slabs at 500 and 700, and more stone and a gem on top. upper = true opens every other object inside the building's footprint whose origin stands more than clear above the player's feet; walls standing on the floor the player walks stay.

Those objects are of any type, and only registered types reach the handler, so a map using upper registers all of them and hands whatever no other rule took to Helpers.FadeAboveRoofs:

local ROOFS =
{
	[94] = { sizeX = 150, sizeY = 150, whole = true, upper = true },
}

Helpers.RegisterAll()

Map.OnObject(function(o)
	local roof = ROOFS[o.type]

	if roof then
		Helpers.FadeUnder(o, roof)
		return
	end

	Helpers.FadeAboveRoofs(o)
end)

Registering everything costs one handler call per visible object per frame.

One building differently

Keys on a ROOFS entry apply to every building of that type. per gives one building its own values: each item names a tile inside the building, and whatever it leaves out comes from the entry around it.

[94] =
{
	sizeX = 150, sizeY = 150, whole = true, upper = true,

	per =
	{
		-- The pyramid: a gem and a cobra stand just past its footprint over the entrance.
		{ x = 184, y = 23, early = 100 },
	},
},

early moves both edges at once: the player opens the building that much sooner, and with upper the footprint of what opens with it grows by the same amount - so pieces standing just in front of the roof go too.

By rectangle

For a building whose pieces also stand elsewhere as something else, where fading by type would take them away across the whole map. Helpers.FadeArea(o, area) fades what stands inside a rectangle of tiles, while the player is inside it too.

local BUILDINGS =
{
	{ x1 = 195, y1 = 40, x2 = 199, y2 = 45, types = { [94] = true } },
}
Key Meaning
x1 y1 x2 y2 the rectangle, in the tile coordinates the position readout shows
types only these types fade; without it everything in the rectangle does
minZ only what stands at least this high, in world units
alpha rate as above; defaults 0.1 and 0.12

FadeArea returns nil for an object that is not its own - outside the rectangle or of another type - so rectangles can be tried first and the object handed on to the rules by type:

Map.OnObject(function(o)
	for i = 1, #BUILDINGS do
		if Helpers.FadeArea(o, BUILDINGS[i]) ~= nil then
			return
		end
	end

	local roof = ROOFS[o.type]

	if roof then
		Helpers.FadeUnder(o, roof)
	end
end)

The types still have to be registered, as for ROOFS.

Finding the numbers

  • The type is the model's file number minus one: Object95.bmd is type 94.

  • Heights come from the game itself. Log them once while standing inside:

    local hx, hy, hz = Map.HeroPosition()
    Log.Add(string.format("hero %.0f %.0f %.0f  roof %.0f", hx, hy, hz, o.positionZ))

How it fades

The script writes alpha straight into the object. It only ever brings back what it faded itself, so every other object keeps the distance fog fade even on a map that registers all types. Leave alphaEnable off: it puts the object on a blend path that breaks its shadow and depth.

See Also

Clone this wiki locally