Status: Superseded on 2026-10-29 by Map Character Movement and Pathfinding Proposal. This earlier proposal is retained as design history; its speculative graph schema and position-persistence concepts were not adopted.
The implemented helper startup teleport described below remains in place. The new proposal validates BC’s existing admin-targeted map teleport flow and defines the no-server-change design for moving other room members.
The original design describes a larger navigation feature than the current
runtime supports. The repository already stores room-scoped point/region
locations in veratownLocations and emits location transitions from BC-observed
coordinates. It does not have graph exits, movement permissions/capacity rules,
or a player movement command. Adding the proposed mapLocationDefinitions and
mapCharacterPositions collections would duplicate existing configuration and
make MongoDB compete with BC as the authority for a character’s live position.
Bondage Club exposes ChatRoomCharacterMapDataUpdate for a character map-data
update, followed (when delivered) by ChatRoomSyncMapData. The update has no
request ID or direct acknowledgement. MapViewTeleport is a map-view chat
dictionary tag, not a movement packet. The implementation therefore treats
teleport as an immediate position-update request and reports success only after
the bot’s matching position is observed; it does not claim that BC accepted a
particular operation causally.
BcClient.start() continues to own login, join-or-create, and the room sync
barrier. When a helper agent has an optional startLocationKey, its worker
requests a teleport from the ready event, which occurs only after the matching
room has synchronized. The target must be an enabled point location in the
configured room. The worker does not poll or execute tasks unless the resulting
position is observed (or was already observed at that point). A missing or
unconfirmed teleport fences the worker; it is not blindly retried.
This implementation does not move other characters, choose a location in a region, bypass location enablement, change the room/map template, or persist a second position record. Operators select the location through the existing helper-agent configuration. Live BC qualification is still required before relying on movement in an operational room.
The long-term proposal is a shared movement system for bots and player-controlled characters on the map. Its currently implemented slice is operator-configured same-room helper startup teleport. The graph navigation and player-facing behavior described below is a future design, not implemented runtime behavior.
Long-term target scope:
Explicitly unchanged:
BcClient and its existing send queue; feature code does not emit socket packets.ChatRoomSyncMapData is observed. BC provides no request ID for causal attribution.ChatRoomCharacterMapDataUpdate, preserving its currently observed
PrivateState. The implemented use is restricted to an operator-configured
helper startup point after the initial room sync. It does not traverse exits
or establish that the destination is reachable by walking.The BC MapViewTeleport dictionary tag is not a movement mode or packet.
A room is the top-level container on the BC map. Each room has:
roomKey identifier (e.g., "Bondage Club", "Pillory")."center", "corner-north", "chair-red").A location is a position identifier within a room. It has:
name within that room.category indicating its type: "public", "restricted", "furniture", "player-area", "offstage", etc.metadata: description, visual/sensory properties, capacity limits, or feature associations.An exit is a directional connection from one location to another. It has:
name or direction identifier (e.g., "north", "through-door", "to-kitchen").destination: target room and location."door-unlocked", "admin-only", "member-level >= 2").metadata: flavor text, visual cues, or traversal side effects.At any moment, a location may be accessible or inaccessible based on:
"player-areas-disabled").Movement is denied if any condition fails. The system must report which condition was violated to support user-facing feedback.
The proposed mapCharacterPositions collection is not adopted. The current
room snapshot and its character MapData.Pos, updated only from BC syncs, are
the live position authority. Existing veratownLocations documents store
room configuration, not character state. Any future durable movement intent or
reconciliation record must remain distinct from the current server-observed
coordinate.
A movement attempt is a request to relocate a character from current to target (a room + location pair). The system validates in this order:
If all checks pass, the movement is approved. If any check fails, the system reports the first failure reason and does not update position.
type MovementResult =
| { success: true; from: Location; to: Location; path?: Location[] }
| {
success: false;
reason:
| "no-room-access"
| "invalid-location"
| "blocked-exit"
| "capacity-full"
| "exclusive-occupied"
| "character-restricted";
details?: string; // e.g., "door requires admin permission"
};
For multi-step movement, use breadth-first search (BFS) to compute the shortest accessible path:
A player issues an explicit movement command using:
/map go <location> — Move to a named location in the current room./map go <room>:<location> — Move to a location in a named room./map path <destination> — Compute and display a multi-step path without moving; optionally move if confirmed.!goto <location> — Shorthand for /map go.Movement is allowed if the player’s character:
Future player commands must show only destinations the requester can access. Any administrative override needs a separately defined policy and must not be assumed to bypass BC room access or feature restrictions.
The implemented bot movement is limited to startup positioning for helper
workers. With startLocationKey configured on the helper-agent document, the
worker requests a same-room teleport after the BcClient ready event. The
target must be an enabled point location, and the worker remains inactive until
BC observes that position. Leaving the field unset preserves existing worker
behavior.
Future bot movement could include:
A patrol is a sequence of locations (in one or more rooms) that a bot repeats:
{
botMemberNumber: number;
name: string;
locations: Array<{ roomKey: string; location: string }>;
intervalMs: number; // e.g., 5 * 60_000 for every 5 minutes
enabled: boolean;
validation?: "strict" | "skip-inaccessible"; // strict fails if any step is blocked; skip-inaccessible retries on next interval
}
Patrols are stored in a mapBotPatrols collection (or feature-scoped equivalent). A bot executes only one active patrol per session. If validation fails, the bot either:
Bots move reactively in response to:
/bot go <location>).Reactive movements use the same validation and pathfinding as player commands. If a reactive move fails, the bot logs the failure and optionally falls back to the nearest accessible location.
When the bot starts or a character reconnects:
mapCharacterPositions collection for the character’s last recorded position."Bondage Club:center") and log the fallback reason.If a position record is older than a configured threshold (e.g., 24 hours offline), optionally flag it for manual review or reset to the default location.
If a location is removed, renamed, or gated off during a game session:
No movement-specific collection is created by the current implementation.
Coordinates are observed from BC; helper startLocationKey is an optional
field in prototypeHelperAgents, and location coordinates continue to come
from the existing veratownLocations configuration. The schemas below are
unimplemented concepts and must not be treated as current runtime storage.
mapCharacterPositions collection{
_id: ObjectId;
memberNumber: number; // unique index
roomKey: string;
location: string;
timestamp: Date; // last update time
movementReason?: string;
lastValidated: Date;
recoveryFallback?: {
roomKey: string;
location: string;
};
}
mapBotPatrols collection{
_id: ObjectId;
botMemberNumber: number;
name: string;
locations: Array<{
roomKey: string;
location: string;
}>;
intervalMs: number;
enabled: boolean;
createdAt: Date;
updatedAt: Date;
lastExecutedAt?: Date;
validationMode: "strict" | "skip-inaccessible";
}
mapLocationDefinitions collection{
_id: ObjectId;
roomKey: string;
location: string; // unique per room
category: string;
metadata?: {
description?: string;
capacity?: number;
exclusive?: boolean;
};
exits: Array<{
name: string;
destination: {
roomKey: string;
location: string;
};
conditions?: Array<string>;
metadata?: object;
}>;
gateFlags?: Array<string>; // e.g., ["player-areas-enabled"]
createdAt: Date;
updatedAt: Date;
}
interface MapMovementPort {
/**
* Validate and perform a movement attempt.
*/
move(
character: { memberNumber: number },
destination: { roomKey: string; location: string },
reason: string
): Promise<MovementResult>;
/**
* Compute a multi-step path between two locations.
*/
findPath(
start: { roomKey: string; location: string },
goal: { roomKey: string; location: string }
): Promise<{ path: Array<{ roomKey: string; location: string }> | null; reachable: Array<...> }>;
/**
* Get the current position of a character.
*/
getPosition(memberNumber: number): Promise<{ roomKey: string; location: string } | null>;
/**
/**
* List all accessible locations from a given position.
*/
getAccessibleExits(
memberNumber: number,
position: { roomKey: string; location: string }
): Promise<Array<{ name: string; destination: { roomKey: string; location: string } }>>;
}
Movement uses the world administration subsystem to:
There is no circular dependency: movement does not modify map definitions; world admin does not directly execute movement (it delegates to the movement port).
Exclusive game sessions mark a character as “in-session” and block movement. When a session ends, position is restored to the pre-session location or a configured fallback.
When movement validation fails, report:
"blocked-exit", "admin-only")."door 'north-gate' is locked").Examples:
If pathfinding fails to find a goal, optionally offer alternatives:
If a bot’s patrol or reactive movement fails:
If a character’s position is invalid or corrupt:
Future enhancements could include: