Grimwar/VSig
VSig is Grimwar's map-logic language. A VSig program listens to entity or game outputs, changes state, calls entity inputs, schedules work, and exchanges bounded signals with VMods. The Map Exporter validates and compiles it into the map package.
Use VSig for map orchestration. Use an entity marker when the thing has a position, volume, renderer, collider, or native game behavior. Use VMod for server rules that need players, commands, persistent storage, permissions, or game-mode control.
Create the map script
A map scene and its VSig source share a name and folder:
Assets/CustomContent/Maps/Range/Range.unity
Assets/CustomContent/Maps/Range/Range.vsig
Create a script with Assets > Create > Vellocet > SDK > VSig Script. A marker Inspector can also create or open the current scene's file and insert refs for selected entities.
Install syntax support from SDK Workbench > Project > VSig Editor Support. Unity still performs the authoritative parse and validation.
First working script
scene "Example"
ref trigger: trigger_multiple match Triggers/Entrance
ref door: func_door match Doors/MainDoor
var door_start: DoorEntityState = null
once on map.ready:
door_start = door.state
on trigger.start_touch:
door.open
on trigger.end_touch_all:
door.restore(door_start)
The two ref declarations bind authored markers to short aliases. map.ready runs after server-side map entities are bound. The trigger's start_touch output calls the door's open input. The script captures the starting door state once and restores it after the last toucher leaves.
Marker Inspectors list the inputs, outputs, state type, and access rules for that entity class. Use that contract instead of guessing a signal name.
Refs and groups
A ref can match one hierarchy path or a wildcard:
ref main_door: func_door match BuildingA/Door
ref spawn_doors: func_door match SpawnDoors/Door*
group exits:
main_door
spawn_doors
on round.post_round:
exits.close
An input sent to a wildcard ref or group broadcasts to every match. Property reads and state capture require exactly one entity.
Stable GameObject names make refs readable and keep wildcard matches predictable. A marker's hidden runtime ID is managed by the SDK; VSig authoring uses the object hierarchy and entity class.
Values and expressions
Primitive types are bool, int, float, and string. Entity state uses the state type declared by its marker contract. Durations accept seconds or milliseconds, including 1.5s and 250ms.
seed 1847
const max_score: int = 10
var score: int = 0
var title: string = "Arena"
on button.pressed:
score = clamp(score + 1, 0, max_score)
label.set_text("{0}: {1}/{2}", title, score, max_score)
Strings must be quoted. Declare and initialize map variables and module state before use. Handler parameters and procedure parameters are immutable; assign mutable data to a map var or module state.
Operators include arithmetic, comparisons, and, or, and not. Built-in functions include:
min,max,clamp,abs,floor,ceil, androundlerpandremaprandom,random_int,shuffle, andchoosetime,format, andconvarbool,int,float, andstringconversions
Without seed, each map runtime starts with fresh random entropy. A declared seed makes random sequences repeatable. shuffle(self, count) gives each module instance its own shuffle bag.
Conditions and matching
on counter.changed(value: float):
if value >= 10:
door.open
elif value > 0:
light.on
else:
light.off
match int(value):
case 1:
label.set_text("one")
case 2:
label.set_text("two")
default:
label.set_text("many")
Indentation defines the statement block. A type mismatch, unknown name, invalid call, missing ref, or wrong handler payload is a compile error.
Procedures and schedules
def announce(message: string, delay: float = 0s):
wait delay
label.set_text(message)
on round.live:
call announce("Fight!", 0.25s)
schedule heartbeat every 1s times 10:
light.toggle
on round.post_round:
cancel heartbeat
wait suspends the current handler or procedure. schedule name after duration runs once. schedule name every duration repeats, and times limits the count. Starting a named schedule replaces the existing task with that name. Cancelling a task that is not running is safe.
Cancel round-owned repeating work on round.post_round or another lifecycle event that always closes the activity.
Reusable modules
A module contains parameters, private state, inputs, outputs, procedures, and handlers:
module counter(start: float = 0, max_value: float = 10):
state value: float = start
output changed(value: float)
output reached(value: float)
input add(amount: float = 1):
value = min(value + amount, max_value)
emit changed(value)
if value >= max_value:
emit reached(value)
input reset:
value = start
emit changed(value)
use counter as captures(max_value = 3)
ref capture_point: game_capture_point
ref exit: func_door
on capture_point.captured:
captures.add
on captures.reached(value: float):
exit.open
Module parameters are immutable configuration. State is private mutable data and can be inspected as instance.state_name. Inputs are typed procedures. Outputs carry typed payloads and work like entity outputs.
Put shared modules in .vsiglib files and import only the required module:
from "Packages/com.vellocet.sdk/Runtime/VSig/Libraries/Logic.vsiglib" use counter
use counter as captures(max_value = 5)
The standard logic library contains counter, accumulator, relay, timer, selector, random_selector, sequence, latch, toggle, state_machine, debounce, and cooldown.
These modules are compiled logic. They do not create GameObjects or network objects.
Spatial modules and logic_script
A spatial module declares local refs and binds each instance to a scene hierarchy:
module gate:
ref trigger: trigger_multiple
ref door: func_door
on trigger.start_touch:
door.open
input reset:
door.close
use gate as exits match Gates/Exit_*
A wildcard use creates one module instance per matching root and a broadcast group named by the authored alias.
Place a logic_script marker when a designer should choose a module and its arguments in the scene Inspector. Select the .vsiglib, module name, alias, arguments, and local-ref binding mode. The compiler expands the marker into the same form as a source-authored use. It does not become a runtime entity.
Map and round lifecycle
Lifecycle sources need no scene marker:
once on map.ready:
doors.close
on round.warmup:
warmup_lights.enable
on round.pre_round:
round_timer.reset
on round.live:
round_timer.start
on round.overtime:
overtime_alarm.play
on round.post_round:
cancel heartbeat
on round.intermission:
doors.open
Available lifecycle sources are map.ready, round.warmup, round.pre_round, round.live, round.overtime, round.post_round, and round.intermission.
Use once on map.ready to capture an authored entity's initial state or perform setup that must run once per map load.
Safe server commands
server_command() sends one command through the server's content-accessible command boundary:
once on map.ready:
server_command("mp_roundtime 3")
server_command("sv_gravity -6")
server_command("say Map rules loaded")
on door.open:
server_command("gm_message major DOOR HAS BEEN OPENED")
The server allows a curated set of match timing, chat or HUD messages, round completion, friendly fire, movement tuning, class enforcement, and weapon-slot rules. It rejects semicolons, newlines, secret-like identifiers, and commands without the ContentAccessible flag. Any changed console variable is restored when the map unloads.
Server operators can disable map commands with sv_allow_content_commands 0. convar() reads use the same allowlist.
VMods can emit a source under mod:<mod-id>. Handle that signal like any other output:
on mod:community.hello.round_started(mode_id: string):
status_label.set_text("Mode: {0}", mode_id)
on mod:community.hello.stage.opened(index: int, gate_name: string):
gate_counter.set(index)
The first handler receives output round_started from source mod:community.hello. The second receives output opened from child source mod:community.hello.stage.
A VMod can subscribe to a map entity or module output by source alias and output name. This exchange carries scalar values only. It does not expose scene objects to VMod code.
Compile and debug
Press Play for quick testing. The compiled VSig runtime Inspector shows validation state, live module state, scheduled tasks, and a bounded recent-event trace. Marker Inspectors can show signal routes and add selected refs or a group to the paired script.
Before export:
- Save the scene and VSig file.
- Resolve parser, type, ref, module, and entity-contract diagnostics.
- Check wildcard refs against the final hierarchy.
- Exercise each round-phase handler more than once.
- Confirm repeating schedules stop at the intended lifecycle boundary.
- Run Compile Scene in the Map Exporter before building the addon package.
The exporter expands imported modules, validates the final program, and embeds a compact compiled program in the map. A Release export refuses incomplete required bakes or invalid VSig.