NPC and monster navigation collision

From Wiki G1R-MP G1 Remake Multiplayer
Jump to navigation Jump to search


AVAILABLE FROM UPDATE 0.1.2
This navigation-collision system and the APIs described below are available in G1R:MP 0.1.2 and later.

NPCs and monsters share one authoritative server navigation-collision service. It combines two selectable static layers with mandatory dynamic character separation:

  • builtin: verified MainMap blockers baked into the server package;
  • custom: bounded blockers created by server-side Lua resources;
  • dynamic capsules: connected players, NPCs and monsters; always enabled.

The baked MainMap layer is a conservative 2.5D no-go representation. It is not a Recast navmesh or terrain heightfield. It includes verified navigation-exclusion volumes and exact supported collision primitives for relevant world objects such as tree trunks; it does not infer blockers from broad mesh bounds or tree crowns.

Collision-source modes

Every NPC and monster uses combined by default:

Mode Static collision sources
combined Baked MainMap blockers and enabled custom blockers
builtin Baked MainMap blockers only
custom Enabled custom blockers only

Mode values are case-sensitive. Select the mode at spawn time when the initial position must be validated against a specific layer:

local monsterId = spawnMonster("scavenger", 20, 5, 2, 0, {
    navigationCollisionMode = "custom",
})

local npcId = spawnNpc("Gate Guard", "guard_01", 24, 5, 2, 180, {
    navigationCollisionMode = "combined",
})

The mode can be changed later:

setMonsterNavigationCollisionMode(monsterId, "builtin")
setNpcNavigationCollisionMode(npcId, "custom")

The current value is exposed as getMonsterData(monsterId).navigationCollisionMode or getNpcData(npcId).navigationCollisionMode.

navigationCollisionMode is independent from navigationMode. Collision mode selects the static geometry that must be respected. Navigation mode selects built-in AI decisions or direct scripted movement goals. Player, NPC and monster capsule separation remains active in every combination, and clients never become authoritative.

Changing a collision mode invalidates the entity's cached path. Material custom-blocker changes selectively revalidate affected cached paths; spatially unrelated paths are not globally rebuilt.

Resource-owned blocker API

createAiNavigationBlocker(definition)              -- string ID or false
updateAiNavigationBlocker(blockerId, definition)   -- bool
setAiNavigationBlockerEnabled(blockerId, enabled)  -- bool
destroyAiNavigationBlocker(blockerId)              -- bool
getAiNavigationBlockerData(blockerId)              -- table or false
getAiNavigationBlockers()                          -- table

Blocker IDs are opaque decimal strings and must not be converted to Lua 5.1 numbers. A resource can inspect or mutate only blockers it created. Enabled geometry participates in the shared custom layer seen by all AI entities that use custom or combined. Stopping or restarting a resource removes all blockers owned by that resource.

All coordinates and dimensions use metres. Supported definitions are boxes, cylinders and simple convex polygons:

local gate = createAiNavigationBlocker({
    shape = "box",
    x = 125.0, y = -48.0, z = 2.0,
    width = 2.5, depth = 0.8, height = 4.0,
    yaw = 30,
    enabled = true,
})

local pillar = createAiNavigationBlocker({
    shape = "cylinder",
    x = 130.0, y = -45.0, z = 2.0,
    radius = 1.5, height = 4.0,
})

local restrictedArea = createAiNavigationBlocker({
    shape = "polygon",
    points = {
        { x = 10.0, y = 10.0 },
        { x = 14.0, y = 10.0 },
        { x = 14.0, y = 13.0 },
        { x = 10.0, y = 13.0 },
    },
    minZ = -1.0,
    maxZ = 5.0,
})

Shape names are case-sensitive. Dimensions must be finite and between 0.01 and 10,000 metres. Polygon definitions accept 3-16 finite vertices and must be simple and convex. World coordinates are bounded to +/-100,000 metres.

Getters return canonical polygon data even when the input shape was a box or cylinder. getAiNavigationBlockers() is sorted deterministically by ID and includes disabled blockers. Omitting enabled from an update preserves the current state.

Runtime limits and intended use

The default hard limits are:

  • 1,024 blockers per resource;
  • 4,096 custom blockers across the server;
  • 4,096 broad-phase cells per blocker;
  • 1,000,000 combined custom spatial-index entries.

Invalid or over-limit creates and updates return false without partially changing geometry.

Custom blockers affect AI navigation only. They do not create visible meshes, Unreal physics objects, player collision, or client-side world geometry. They are intended for static geometry and occasional changes such as opening or closing a gate. Do not move or toggle them every server tick; use the mandatory dynamic capsule layer for moving players, NPCs and monsters.