Map

Map extends MutableShape, adding functions and properties to it.
Map is not creatable, there's only one instance of it. It can only be accessed through its globally exposed variable.

Map is a global variable that represents the game map.

How to load a map for your game?

Config = {
  Map = "aduermael.rockies"
}
Config.Map = "aduermael.rockies"

(See Config for more details regarding Map & Items loading)

Functions

Inherited from MutableShape

Hide

Adds a Block to the Map. You may provide a Block, a palette index to an existing color in Map's Palette, or any color which will be added automatically to the Map's Palette if needed.

Returns true if a block was successfully added.

-- providing an existing palette index
someMutableShape:AddBlock(1, 10, 10, 10)

-- providing a new color index
local newColorIdx = someMutableShape.Palette:AddColor(Color(200, 0, 200))
someMutableShape:AddBlock(newColorIdx, 10, 10, 10)

-- providing a Block instance
local block = Block(Color(200, 0, 200), 10, 10, 10)
someMutableShape:AddBlock(block)

-- returns whether the block was added or not:
local added = someMutableShape:AddBlock(1, 10, 10, 10)
if added ~= false then
  -- block successfully created!
end

Gets a Block from the Map.
Returns nil if there is no Block at the given coordinates (i. e. if it's "air").

Inherited from Object

Hide

nil Load ( string itemName, function callback, table config optional )

Loads the given item asynchronously and calls the callback once done. The parameter itemName follows the usual naming convention user.item.

This is a function of the global Map, to be called as Object:Load(itemName, callback, config).

The config table options are as follows,
- mutable allows to create the item shapes as MutableShape instead of Shape. Default false.
- bakedLight allows to generate baked lighting for the item shapes. Default false.

nil AddChild ( Object child, boolean keepWorld optional )

Adds given Object as a child. Object extensions like Shape or MutableShape are naturally accepted too.

The keepWorld optional parameter, false by default, dictates whether to maintain the child's world or local position and rotation. Keeping world will ensure the object doesn't move in the scene, adjusting its local position/rotation accordingly; keeping local will have the object move in the scene in order to maintain an equivalent local position/rotation relative to its new parent.

It's a good practice to set child/parent relationships before setting positions, if you do not want to use the keepWorld parameter.

Note that there is a cyclic hierarchy protection that will prevent you from creating parenting loops.

local o = Object()
local myShape = Shape(Items.someuser.someitem)
o:AddChild(myShape)
nil RemoveChild ( Object child, boolean keepWorld optional )

Unsets parent/child relationship with child parameter. The child ends up being deleted if it has no other references.

The keepWorld optional parameter, false by default, dictates whether to maintain the child's world or local position and rotation. Keeping world will ensure the object doesn't move in the scene, adjusting its local position/rotation accordingly; keeping local will have the object move in the scene in order to maintain an equivalent local position/rotation relative to its new parent.

o:RemoveChild(someChildObject)
nil RemoveChildren ( boolean keepWorld optional )

Unsets parent/child relationship with all children. Individual children end up being deleted if they have no other references.

The keepWorld optional parameter, false by default, dictates whether to maintain the child's world or local position and rotation. Keeping world will ensure the object doesn't move in the scene, adjusting its local position/rotation accordingly; keeping local will have the object move in the scene in order to maintain an equivalent local position/rotation relative to its new parent.

o:RemoveChildren()
nil Recurse ( function callback, table config optional )

Iterates over all descendants of Map and calls the callback function for each.

The callback function is called with the descendant as the only argument.

The config table accepts two boolean options:
- deepFirst: if true, traverses depth-first instead of root-first. Default false.
- includeRoot: if true, includes Map in the recursion. Default true.

o:Recurse(function(descendant)
  print(descendant)
end, { includeRoot = true })
Object FindFirst ( function searchFunction, table config optional )

Returns the first descendant Object for which the search function returns true.
If no descendant is found, returns nil.

The search function is called with the descendant as the only argument.

The config table accepts two boolean options:
- deepFirst: if true, traverses depth-first instead of root-first. Default false.
- includeRoot: if true, includes Map in the recursion. Default false.

local leaf = oakTree:FindFirst(function(o) return o.Name == "leaf" end)
if leaf then
  print("found a leaf:", leaf)
end
table Find ( function searchFunction, table config optional )

Returns all descendants Objects for which the search function returns true.
If no descendant is found, returns an empty table.

The search function is called with the descendant as the only argument.

The config table accepts two boolean options:
- deepFirst: if true, traverses depth-first instead of root-first. Default false.
- includeRoot: if true, includes Map in the recursion. Default false.

local leaves = oakTree:Find(function(o) return o.Name == "leaf" end)
for i, leaf in leaves do
  print("leaf #" .. i .. ":", leaf)
end

The most efficient way to dispose of an Object for good.

Calling Destroy removes the Object and all its descendants from the World.
Metadata associated with all of these Objects also gets removed.

Once destroyed, the Objects can't be used anymore.

o:Destroy()

Get child Object at index.

if o.ChildrenCount > 0 then
  print(o:GetChild(1)) -- prints first child
end

Get Map's parent.

print(myObject:GetParent())

Returns true if the two Objects may collide with each other.

Resets to Map's original collision box. For example, Player and Shape objects will revert to fitting their model bounding box.

nil Recurse ( function callback, table config optional )

Iterates over all descendants of Map and calls the callback function for each.

The callback function is called with the descendant as the only argument.

The config table options are as follows,
- includeRoot whether to include Map in the iteration. Default true.
- depth the maximum depth of the iteration. Default -1 (unlimited).
- deepFirst whether to iterate in depth-first order. Default false.

If you were to remove an object from its parent while recursing through it, the object removal will be delayed until the full recursion is completed.

o:Recurse(function(descendant)
  print(descendant.Name)
end, { includeRoot = true })
nil Copy ( table config optional )

Creates a copy of the object, including all its properties and current state.

To copy a composite object, you may call o:Copy({ recurse = true }). This will copy and replicate the full object hierarchy.

Inherited from Shape

Hide

Gets a Block from the Shape.
Returned Block is read-only because Shape is immutable, unlike MutableShape.
Returns nil if there is no Block at those coordinates (i. e. if it's "air").

Converts given Block coordinates or Number3 from model space to world space.

Converts given Block coordinates or Number3 from model space to local space.

Converts given Number3 from world space to model space.

Converts given Number3 from local space to model space.

Computes and returns the smallest axis-aligned box that encompasses all of Map's blocks, in local space (i.e. expressed from This.Pivot).

Computes and returns the smallest axis-aligned box that encompasses all of Map's blocks, in world space (i.e. expressed from the origin of the world, which corresponds to the World object).

Computes the shape baked lighting. It is a combination of a white ambient light and all blocks that were set as light sources. Other shapes entering inside this shape's bounding box will be affected by its baked lighting.

This is an efficient way of applying lighting to shapes without affecting performance. The baked lighting of a shape is cached to speed up subsequent loads.

Once a shape has baked lighting, it will be automatically maintained when changing its blocks.

However, directly changing the transparency or light properties of shape's Palette entries of _existing_ blocks will not be reflected on the shape immediately, and will require another call to this function.

You may want to call this function only if:
- activating baked lighting on a shape for the first time
- setting light property i.e. shape.Palettei.Light = true/false of _existing_ shape blocks
- setting transparency i.e. shape.Palettei.Color.A = newValue of _existing_ shape blocks

You _do not_ need to call this function if:
- ANY blocks, including light and transparent blocks, are added/removed at runtime
- setting light or transparent property of an unused palette entry first, before adding blocks using that entry

Client.OnStart = function()
  Map.Palette[1].Light = true

  -- refresh baked lighting for existing blocks
  Map:ComputeBakedLight(function() print("Map baked lighting done!") end)
end

Removes the shape baked lighting and frees up any memory used. It could be an optimization step for scenes pooling a large amount of shapes.

Properties

Inherited from Object

Hide

Collision groups the Map belongs to.

⚠️ It doesn't mean the Map will collide with other Objects in these groups.

If the Map belongs to group number 3 for example, it means all Objects that have group number 3 in their Object.CollidesWithGroups property will collide with it.

By default:
- Objects collide with the Map and other Objects
- Players collide with the Map only

That can all be configured differently depending on your needs.

Collision groups are best used as buckets created for gameplay needs, for example,
- a group for "hittable" objects that can be damaged by the player's weapon
- a group for recyclable Objects that are disabled and thrown back into their pool when they reach a certain trigger collider
- a group for static scenery objects that moving objects will bump into
- a group for objects that can get hit by raycasts executed for some specific game mechanic
It may well be the case that one object belongs to several or all of these groups, depending on the gameplay associated with it.

In general, it is bad practice to assign or add all collision groups from one object to another (unless making exact copies), as it isn't future-proof if you later add more groups. Instead, you should assign individual groups depending on what this new object should contribute to in your world.

-- these are the group numbers assigned by default to Map, Player, and all Objects
local GROUP_MAP = 1
local GROUP_PLAYER = 2
local GROUP_OBJECT = 3

-- by default, this object will collide with the map and other objects
local o = Object()
o.Physics = PhysicsMode.Dynamic

-- when in doubt, print the groups of your objects
print(Map.CollisionGroups, Map.CollidesWithGroups)
print(o.CollisionGroups, o.CollidesWithGroups)

-- let's say our world needs an extra group for a gameplay mechanic, for example, a group for objects that can be hit by player's weapon (which could perform a raycast in that group)
local GROUP_HITTABLE = 4

-- here, the object would collide with anything that cares about GROUP_OBJECT or GROUP_HITTABLE, in addition to itself wanting to collide with GROUP_MAP and GROUP_PLAYER
o.CollisionGroups = { GROUP_OBJECT, GROUP_HITTABLE }
o.CollidesWithGroups = { GROUP_MAP, GROUP_PLAYER }

-- when adding new obstacle/scenery objects to our scene, you can simply re-use the GROUP_MAP as a way to say "this is part of the environment that is collidable"
-- here, maybe this object only acts as an obstacle and doesn't contribute to anything else
someBigRock.CollisionGroups = GROUP_MAP
someBigRock.CollidesWithGroups = nil

-- doing this effectively prevents all physics interaction on an object
o.CollisionGroups = nil
o.CollidesWithGroups = nil

-- note that there is redundancy with CollisionGroups and CollidesWithGroups, so you can organize your groups however seems more convenient for you
-- for example the following setups will all result in object1 and object2 colliding with each other
object1.CollisionGroups = GROUP_OBJECT
object1.CollidesWithGroups = nil
object2.CollisionGroups = GROUP_OBJECT
object2.CollidesWithGroups = GROUP_OBJECT

object1.CollisionGroups = GROUP_OBJECT
object1.CollidesWithGroups = GROUP_OBJECT
object2.CollisionGroups = GROUP_OBJECT
object2.CollidesWithGroups = nil

object1.CollisionGroups = GROUP_OBJECT
object1.CollidesWithGroups = GROUP_OBJECT
object2.CollisionGroups = GROUP_OBJECT
object2.CollidesWithGroups = GROUP_OBJECT

object1.CollisionGroups = GROUP_OBJECT
object1.CollidesWithGroups = nil
object2.CollisionGroups = nil
object2.CollidesWithGroups = GROUP_OBJECT

Collision groups the Map collides with. See Map.CollisionGroups.

By default:
- Objects collide with the Map and other Objects
- Players collide with the Map and the Objects

That can all be configured differently depending on your needs.

local object = Object()

-- It's not mandatory to change Physics value.
-- (default value is PhysicsMode.Static)
-- An object with Physics set to PhysicsMode.Static contributes 
-- to the physics simulation as a static item (can't be moved)
object.Physics = PhysicsMode.Dynamic

-- making an object collide with the Map and Players
object.CollidesWithGroups = Map.CollisionGroups + Player.CollisionGroups

-- for an Object to collide with other objects only
-- (won't collide with the map)
object.CollidesWithGroups = object.CollisionGroups

-- for Player (local player) to collide with other players and the Map
Player.CollidesWithGroups = Map.CollisionGroups + Player.CollisionGroups

-- making sure 2 objects collide with each others
-- NOTE: by default:
-- Map.CollisionGroups == {1},
-- Player.CollisionGroups == {2},
-- Object.CollisionGroups == {3}
local object1 = Object()
local object2 = Object()
object1.CollisionGroups = {5}
object2.CollisionGroups = {5}
object1.CollidesWithGroups = {1, 5} -- collides with Map + objects in group 5
object2.CollidesWithGroups = {1, 5} -- collides with Map + objects in group 5

-- would also work this way if you don't 
-- remember Map's group (which can be changed too by the way)
object1.CollidesWithGroups = Map.CollisionGroups + {5}

nil by default. Can be set to a function that will be triggered when this object begins a collision with another object.

The function is called with 3 parameters:
- the object the callback was set for,
- the other actor in the collision,
- the world normal of the hit surface.

Note: it's not necessary to use all 3 parameters.

object.OnCollisionBegin = function(self, other, normal)
  print("collision began between", self, " and ", other, " with world normal ", normal)
end

nil by default. Can be set to a function that will be triggered when the Map ends colliding with another Object.

The function is called with 2 parameters: the object the callback was set for and the other actor in the collision.

object.OnCollisionEnd = function(self, other)
  print("collision ended between", self, "and", other)
end

Can be set to true for the Map to be hidden recursively, meaning Map and all of its children are hidden.

Nothing else changes, the Map remains in the scene and it keeps being affected by the simulation (collisions, etc.).

Can be set to true for the Map to be hidden individually.

Nothing else changes, the Map remains in the scene and it keeps being affected by the simulation (collisions, etc.).

Size in world units of the shadow cookie projected under the Map, default is 0.0 (disabled).
The shadow cookie, also called blob shadow, is a square texture acting as a cheap alternative to projected shadows.

If this value is strictly positive, shadow cookies will be displayed when:
- the scene has no light source,
- the scene has light sources, but they are disabled because the client is using lower quality settings

Shadow cookies can be used as a fallback to your scene shadows for players with low quality settings, of course, you can also use them instead of shadows as a design choice.

Tick is a function executed each frame when set (nil by default). Provides the Map and elapsed time in seconds as parameters.

For general purposes, you may consider using Client.Tick instead. It is functionally the same, but is executed once for your world per frame, rather than once per frame per object.

The exact number of frames per second may fluctuate and depend on the device. It is typically around 60 frames per second.

myObject.Tick = function(object, dt)
  print("elapsed:", dt, "seconds since last frame")
end
Number3 LossyScale read-only

Convenience property that attempts to match the actual world scale as much as it can. Note that Objects that have multiple levels of nested rotations and scales will return a skewed lossy scale.

The mass of the Object determines how much a given force can move it and whether or not another object can be pushed by it. It cannot be zero, a neutral mass is a mass of 1.

The combined friction of 2 Objects in contact represents how much the moving Object will be able to slide along the colliding Object.

It is a rate between 0 (full slide, no friction) and 1 (maximum friction). Values equal to or lower than 0 will keep or increase momentum, like sliding on ice. Values higher than 1 means a faster stop, up to a value of 2 to ensure a full stop on contact regardless of the colliding Object's own friction.

[Object.Friction] can be set per-face by providing a table with any combination of the following keys : right, left, front, back, top, bottom, other.
For example, to set the friction on the bottom face of an object's collider to 0 and 0.2 on every other faces, you could set, object.Friction = { bottom=0, other=0.2 }.

The combined bounciness of 2 Objects in contact represents how much of the moving Object's velocity is produced after being in contact with the colliding Object, it is a rate between 0 (no bounce) and 1 (100% of the velocity bounced). Values higher than 1 are allowed and will create an increasing momentum at each bounce (try at your own risk).

[Object.Bounciness] can be set per-face by providing a table with any combination of the following keys : right, left, front, back, top, bottom, other.
For example, to set the bounciness on the side faces of an object's collider to 0.2 and 0 on top and bottom faces, you could set, object.Bounciness = { top=0, bottom=0, other=0.2 }.

Returns number of child Objects.

Inherited from Shape

Hide

Palette is an array of BlockProperties, with each entry corresponding to a style of block used by the Map's model. Each of the Map's block use a palette index, indicating which entry to use to draw that block.

If Map.Palette is set, the shape will start using the new palette in place. Each of its block's palette index will then point to the BlockProperties of that new palette. This can be used to share a single palette between multiple shapes, for example to create effects by changing color on a whole group of shapes at once.
Note that if you would like to keep the original colors of the shape while assigning another palette, you should use the Palette.Merge function instead.

number Depth read-only

Returns Map's depth, expressed in model space (the space where blocks are placed). Shortcut to Map.BoundingBox.Size.Z.

number Height read-only

Returns Map's height, expressed in model space (the space where blocks are placed). Shortcut to Map.BoundingBox.Size.Y.

number Width read-only

Returns Map's width, expressed in model space (the space where blocks are placed). Shortcut to Map.BoundingBox.Size.X.

Number3 Size read-only

Returns Map's model bounding box size, expressed in model space (the space where blocks are placed). Shortcut to Map.BoundingBox.Size.

Box BoundingBox read-only

The bounding box represents the bounds of the Map in model space (the space where blocks are placed).

It is the smallest axis-aligned box that encompasses all of Map's blocks.

If you are looking for the bounding box in a different space, see Map.ComputeLocalBoundingBox and Map.ComputeWorldBoundingBox.

Number3 Min read-only

The minimum point of the Map's bounding box. Shortcut to Map.BoundingBox.Min.

Number3 Center read-only

The center of the Map's bounding box. Shortcut to Map.BoundingBox.Center.

Number3 Max read-only

The maximum point of the Map's bounding box. Shortcut to Map.BoundingBox.Max.

integer BlocksCount read-only

The number of blocks in Map's model.

Whether or not the shape should cast shadows onto other lit objects. Light objects set as shadow casters will affect all shapes in matching layers (see Light.CastsShadows).

Note that whether or not the shape is affected by lights and shadows from other objects depends on the separate property Map.IsUnlit.

Integer or table of integers between 1 and 12. Cameras only render shapes corresponding to their layers, and lights only affect shapes in matching layers.

Whether or not the shape should ignore scene lighting, false by default. If true, the shape won't be affected by any light and shadows from other objects.

Whether or not inner faces between blocks of different colors should be drawn for this shape, true by default.

Original item name of this shape. If it was created programmatically, item name is nil.

Enable and configure draw modes using this property. Setting this to nil disables all draw modes.

Currently, one draw mode is supported: { color= },
- color as a Color, applied multiplicatively to the whole shape. Alternatively, it can be set to a table of options (see below).

Color options are,
- multiplicative as a Color, applied multiplicatively to the whole shape.
- additive as a Color, applied additively to the whole shape.

The additive and multiplicative colors are a shortcut to modify shape colors as a whole, but it could also be done by adding or multiplying each colors of the shape palette.

All these options can be set at runtime or animated individually.