# SCRIPTED EVENTS RELOADED (SER) - Language Specification v1.0.0
## Core Concepts
SER is a scripting language for SCP: Secret Laboratory that allows you to automate game events and create custom commands. Every script is fundamentally a sequence of **methods** (commands) that tell the game what to do.
### The Four Data Types
Every value in SER belongs to one of four categories. When using a **variable**, you must use its specific prefix so the engine knows the data type.
**Literal (`$`)** - Numbers, text, time, booleans, colors, enums
- Examples: `10`, `5s`, `"Scientist"`, `true`, `ff00ffA5`
> `EnumValue` is a subclass of `TextValue` - enums are just text values. User cannot create an `EnumValue`, it only contains metadata about the enum type.
**Player (`@`)** - Array of players
- Examples: `@sender`, `@all`, `@evAttacker`
> Player value acts like an array of players, but if it only has one player, its behavior changes as if it were a singular player object, allowing for property retrieval and more.
**Reference (`*`)** - Game objects like rooms or items
- Examples: `*spawnRoom`, `*evRoom`
**Collection (`&`)** - A list of multiple things
- Examples: `&inventory`, `&rooms`
---
## Methods
Methods are commands that perform actions. Write the method name in `PascalCase`, followed by its arguments.
```
MethodName Arg1 Arg2
Broadcast @all 5s "Hello, world!"
Kill @classDPlayers
GiveItem @sender KeycardO5
```
### Assigning Variables from Methods
You can store method return values in variables:
```
$name = ServerInfo name
@newPlrs = LimitPlayers @all @classDPlayers
```
### The Wildcard (`*`)
Use `*` to specify ALL of something:
```
# kills all players
Kill *
# closes all doors
CloseDoor *
```
### Omitting Optional Arguments
Use `_` to skip optional arguments:
```
*embed = DiscordEmbed "Title" _ _ "Author"
```
---
## Properties and the Arrow Operator (`->`)
Properties allow you to access internal data of values using the `->` operator. This works with:
- Player variables: `@plr -> name`, `@plr -> role`, `@plr -> health`
- Reference variables: `*item -> type`, `*room -> name`
- Collection variables: `&coll -> length`, `&coll -> first`
- Literal values: `$text -> length`, `$num -> abs`
### Player Value Property Access
To access properties of a player value, there MUST ONLY be ONE PLAYER!
There is no "length" property of player value, use `AmountOf` method.
### Chaining Properties
You can chain property accesses together:
```
$nameLengthOdd = @sender -> name -> length -> isOdd
```
### Enum Values vs String Representations
When using enum values in methods vs properties, note the type difference:
**In methods** - Use bare enum tokens (unquoted):
```
SetRole @plr ClassD
```
This is still allowed:
```
SetRole @plr "ClassD"
$role = "ClassD"
SetRole @plr $role
```
**In properties/conditions** - Enums are converted to strings (quoted):
```
if {@plr -> role} is "ClassD"
...
end
```
This difference exists because methods can parse bare tokens as enum values, while expressions require quoted strings for safety and clarity.
The enum is automatically converted to its string representation when accessed as a property.
### Reference Validity
References are pointers to game objects. A reference can become **invalid** (null) if:
- The object no longer exists (e.g., a room was destroyed)
- The player is in a state where the reference doesn't apply (e.g., `@plr -> roomRef` is null when the player is a spectator)
- The object was deleted during script execution
Always validate references before using them:
```
*room = @plr -> roomRef
if {ValidRef *room} is false
Print "Player is not in a room"
stop
end
# Safe to use *room now
CloseDoor *room
```
The `ValidRef` method checks if a reference is null. If it returns false, the reference cannot be used.
### Using Properties in Different Contexts
**In variable definitions** - No brackets needed:
```
$name = @plr -> name
$health = @plr -> health
```
**In conditions** - Use brackets:
```
if {@plr -> role} is "ClassD"
...
end
```
**In text interpolation** - Use brackets:
```
Print "Player: {@plr -> name}"
```
---
## Variables
### Variable Naming
A variable's name must always include its type prefix:
- `$name` (Literal)
- `@name` (Player)
- `*name` (Reference)
- `&name` (Collection)
### Variable Assignment
Create or update variables using `=`:
```
# static value
$kills = 0
# variable
@target = @sender
# math
$kills = $kills + 1
# method return value
$name = ServerInfo name
# function return value
&items = run &GetItems @plr true
```
### Default Values
```
$name = "" # empty text
$num = 0 # zero
$bool = false # false
@plrs = @empty # empty player array
&coll = EmptyCollection # empty collection
```
### Memory Scopes
Variables live in different "buckets" that determine their lifespan:
**Local** - Deleted when the script finishes. Accessible anywhere in the script.
```
$var = 10
```
**Global** - Stays in memory for the entire round. Accessible by other scripts.
```
global $score = 100
```
To read a global variable later, just use its name (no `global` keyword):
```
Broadcast @all 5s "Score: {$score}"
```
To change the value of a global variable, you MUST use `global`:
```
global $number = 1
global $number = $number * 2
```
Not using `global` when changing value makes LOCAL variable with the same name, causing NAME COLLISION error.
**Ephemeral** - Only exists inside a specific loop or function (defined with `with`).
Preferred way:
```
over @all with @plr
# @plr only exists here
end
```
Other valid way: (carried over from previous versions)
```
over @all
with @plr
# @plr only exists here
end
```
### Important: Always Verify Global Variables Exist
Never assume a global variable exists. Always check first:
```
if {VarExists $myGlobal} is false
stop
end
```
---
## Math Expressions
```
$five = 2 + 3
$result = $health + 20
$damage = $baseDamage * 1.5
```
You can use standard operators: `+`, `-`, `*`, `/`, `%`
Math expressions are handled via old NCalc (1.3.8) - refer to NCalc knowledge about type coercion.
### Negative Values
Negative values don't require parentheses in methods:
```
$num = -21
TPPosition @fighter -37 313 -140
```
This is because the whitespace seperates these values into different arguments.
### Percent Sign
The `%` suffix is a valid number modifier that divides by 100:
```
$val = 100% # becomes 1
$chance = 50% # becomes 0.5
```
---
## Text and String Interpolation
### Basic Text
Text is enclosed in double quotes:
```
Print "Hello, world!"
Broadcast @all 5s "Welcome!"
```
### Text Interpolation with `{}`
Insert values into text using curly braces:
```
$name = "Player"
Print "Hello {$name}" # prints: Hello Player
Print "Players online: {AmountOf @all}" # prints: Players online: 5
Print "Role: {@plr -> role}" # prints: Role: ClassD
```
### Escaping Interpolation
Use `~` before `{}` to prevent interpolation:
```
$var = "hi"
Print "var: {$var}" # prints: var: hi
Print "var: ~{$var}" # prints: var: {$var}
```
### Newlines
Use `
` for newlines:
```
Print "Line 1
Line 2
Line 3"
```
---
## Comments
Comments start with `#`:
```
# Comments must have a space after the pound sign
Print "Hello"
Print "Hello" # Maintain 2 spaces between code and comment if inline
```
---
## Conditional Branching (`if`, `else`, `elif`)
Use `if` to run code only when a condition is met:
```
if $hp < 20 and $healedRecently is false
Broadcast @sender 5s "You are critically injured!"
else
Broadcast @sender 5s "You are healing."
end
```
### Condition Syntax
You can use natural language or symbols:
- `is` / `==` - equals
- `isnt` / `!=` - not equals
- `>` - greater than
- `<` - less than
- `and` / `&&` - logical AND
- `or` / `||` - logical OR
- `not` / `!` - ILLEGAL, DO NOT USE
> `!` is illegal because SER relies heavily on spaces, providing `!$var` would break the compiler
> `not` is illegal because it could confuse `x isnt y` with `x is not y`
### Using Properties in Conditions
Always use brackets when accessing properties in conditions:
```
if {@plr -> role} is "ClassD"
...
end
if {RoundInfo duration} > 10m
...
end
```
### Early Returns
Avoid unnecessary nesting by using early returns:
```
if $invalid is true
stop
end
# rest of script here
```
---
## Loops
### `repeat` - Fixed Iterations
Runs a specific number of times:
```
repeat 5
Print "Hello"
end
```
Allows an iteration variable:
```
repeat 5 with $iter
Print "Current iteration: {$iter}"
end
```
### `while` - Conditional Loop
Runs as long as a condition is true:
```
while $count < 10
$count = $count + 1
end
```
Allows an iteration variable:
```
while $count < 10 with $iter
$count = $count + $iter
end
```
### `over` - Iterate Over Collections
Loops through every item in a collection or player array:
```
over @all with @plr
if {@plr -> role} is "Scientist"
GiveItem @plr KeycardScientist
end
end
```
Allows an iteration variable:
```
over @all with @plr $iter
if {@plr -> name -> length} > $iter
...
end
end
```
### `forever` - Infinite Loop
Runs indefinitely. **Must include `wait` to prevent server freeze:**
```
forever
wait 1s
Print "Still running..."
end
```
Allows an iteration variable:
```
forever with $iter
wait 1s
Print "Waiting: {$iter}"
end
```
### Loop Control
**`break`** - Exit the loop immediately:
```
repeat 10
if $condition is true
break
end
end
```
**`continue`** - Skip to the next iteration:
```
over @all with @plr
if {@plr -> role} is "Spectator"
continue
end
# only runs for non-spectators
end
```
### The `with` Keyword
Name the current item or iteration:
```
over @all with @plr
# @plr is now available
end
repeat 5 with $i
# $i is the current iteration (1-5)
end
```
---
## Waiting and Yielding
### `wait` - Pause Execution
Pause the script for a duration:
```
wait 5s
wait 100ms
wait 1m
```
### `wait_until` - Pause Until Condition
Pause until a condition becomes true:
```
wait_until {AmountOf @all} > 0
wait_until {@plr -> health} < 50
```
---
## Functions
Functions allow you to reuse code. They must be defined before use (hoisted).
### Defining Functions
```
func $GetStatus with $val
if $val > 50
return "Healthy"
else
return "Injured"
end
end
```
### Function Naming Convention
The function name prefix indicates what it returns:
- `func $Name` - returns a literal
- `func @Name` - returns players
- `func *Name` - returns a reference
- `func &Name` - returns a collection
- `func Name` - returns nothing
### Calling Functions
Use `run` to execute a function:
```
$result = run $GetStatus 75
@players = run @GetAllScientists
run PrintMessage
```
### Function Parameters
Parameters are defined with `with` on the line after the function header:
```
func $Add with $a $b
return $a + $b
end
$sum = run $Add 5 3
```
---
## Error Handling
Use `attempt` and `on_error` for try-catch logic:
```
attempt
PlayAudio "invalid speaker" "invalid clip name"
on_error with $msg
Print "Error occurred: {$msg}"
end
```
---
## Script Entry Points - Flags
A script can optionally start with a flag. This must be the **very first line**.
### No Flag (Utility Script)
If there's no `!--` line, the script is a utility. Run it manually via `serrun` or `RunScript`:
### Custom Command Flag
Creates a custom command and binds it to the script. When the command is ran, the script runs as well.
```
!-- CustomCommand heal
-- availableFor Server RemoteAdmin
-- description "Heals the sender"
Heal @sender
Broadcast @sender 5s "You have been healed!"
```
### Event Flag
```
!-- OnEvent PlayerDying
if {@evPlayer -> role} is "ClassD"
IsAllowed false
stop
end
```
**Important:** Flags are only registered when the server starts or when `serreload` command is used. If you modify a script with a flag on a running server, you **must use `serreload`**.
---
## Event Cancellation
For `OnEvent` scripts, you can prevent the game from executing the event:
```
!-- OnEvent Dying
if {@evPlayer -> role} is "ClassD"
IsAllowed false
stop
end
```
### Checking if Event Variables Exist
`OnEvent` flags add variables associated with a given event. (done by reflecting on C#)
But event variables might not always be provided by the C# event:
```
!-- OnEvent Dying
if {VarExists @evAttacker} is false
stop
end
```
---
## Stopping Execution
The `stop` keyword immediately ends the script. No further lines are executed:
```
if $invalid is true
Print "Invalid!"
stop
end
Print "This won't run if invalid"
```
---
## Common Patterns
### Check if Player is SCP
```
if {@plr -> team} is "SCPs"
# player is an SCP
end
```
### Select Random Player
```
@plr = LimitPlayers @all 1
```
### Percentage Chance
```
if {Chance 20%}
# 20% chance to execute
end
```
### Check if Variable Exists
```
if {VarExists $myVar} is false
stop
end
```
---
## Advanced Features
SER includes several advanced systems you should explore:
- **DB System** - JSON-based long-term storage
- **HTTP/JSON** - Web requests and JSON manipulation
- **Collections** - Methods for manipulating lists
- **Discord Webhooks** - Discord integration
- **Player Data** - Dictionary/HashMap attached to players
## TOP 20 ESSENTIAL METHODS
**1. Broadcast** - `Broadcast @players duration "message"` - Sends broadcast to players
**2. Hint** - `Hint @players duration "message"` - Sends hint (different display)
**3. GiveItem** - `GiveItem @players ItemType amount` - Gives items to players
**4. SetRole** - `SetRole @players RoleTypeId flags reason` - Changes player roles
**5. Kill** - `Kill @players reason` - Kills players (optional reason)
**6. Damage** - `Damage @players amount` - Damages players by amount
**7. Heal** - `Heal @players amount` - Heals players (won't exceed max)
**8. TPPlayer** - `TPPlayer @targets @destination` - Teleports to another player
**9. TPPosition** - `TPPosition @players x y z` - Teleports to coordinates
**10. SetHealth** - `SetHealth @players amount` - Sets exact health value
**11. SetMaxHealth** - `SetMaxHealth @players amount` - Sets maximum health
**12. Cassie** - `Cassie jingle/noJingle "announcement" "translation"` - CASSIE announcements
**13. CloseDoor** - `CloseDoor *` - Closes all doors (use * for all)
**14. OpenDoor** - `OpenDoor *` - Opens all doors
**15. LockDoor** - `LockDoor * LockReason` - Locks doors with reason
**16. UnlockDoor** - `UnlockDoor *` - Unlocks doors
**17. GiveEffect** - `GiveEffect @players EffectType duration intensity` - Applies status effects
**18. ClearInventory** - `ClearInventory @players` - Clears player inventory
**19. SetSize** - `SetSize @players x y z` - Changes player size (0.1-10 scale)
**20. Explode** - `Explode @players` - Creates explosion at player
**Utility Methods:**
- **AmountOf** - `AmountOf @players` - Returns count of players
- **RoundLock** - `RoundLock true/false` - Locks/unlocks round
- **LobbyLock** - `LobbyLock true/false` - Locks/unlocks lobby
- **SetPlayerData** - `SetPlayerData @player "key" value` - Stores custom player data
- **GetPlayerData** - `GetPlayerData @player "key"` - Retrieves custom player data
- **HasPlayerData** - `HasPlayerData @player "key"` - Checks if data exists
- **LimitPlayers** - `LimitPlayers @players count` - Returns random subset
- **RandomNum** - `RandomNum min max type` - Random number (int/real)
- **Chance** - `Chance percentage` - Returns true with given chance
- **VarExists** - `VarExists $variable` - Checks if variable exists
- **ValidRef** - `ValidRef *reference` - Checks if reference is valid
## TOP 5 ESSENTIAL EVENTS
**1. RoundStarted** - `!-- OnEvent RoundStarted` - Triggers when round starts
- **Use:** Setup scripts, initial item distribution, round-specific events
- **Variables:** None
**2. Death** - `!-- OnEvent Death` - Triggers when player dies
- **Use:** Kill streaks, death rewards, statistics tracking
- **Variables:** `@evPlayer` (victim), `@evAttacker` (killer), `$evOldRole`, `*evOldPosition`
**3. Hurt** - `!-- OnEvent Hurt` - Triggers when player takes damage
- **Use:** Damage tracking, special damage effects, hit reactions
- **Variables:** `@evPlayer` (victim), `@evAttacker` (damager), `$evDamage`
**4. Joined** - `!-- OnEvent Joined` - Triggers when player joins server
- **Use:** Welcome messages, initial setup, tutorial hints
- **Variables:** `@evPlayer` (joining player)
**5. ChangedRole** - `!-- OnEvent ChangedRole` - Triggers when role changes
- **Use:** Role-specific setups, class transitions, spawn effects
- **Variables:** `@evPlayer`, `$evOldRole`, `$evNewRole`
## COMMON PATTERNS
**Random Player Selection:**
```ser
@randomPlayer = LimitPlayers @all 1
```
**Percentage Chance:**
```ser
if {Chance 25%}
# 25% chance to execute
end
```
**Check if Player is SCP:**
```ser
if {@player -> team} is "SCPs"
# Player is SCP
end
```
**Early Return Pattern:**
```ser
if $invalid is true
stop
end
```
**Player Data Storage:**
```ser
SetPlayerData @player "kills" 5
$kills = GetPlayerData @player "kills"
```
**Door Control:**
```ser
CloseDoor *
LockDoor * NoPower
wait 10s
UnlockDoor *
```
**Teleport to Room:**
```ser
*room = GetRoomByName "RoomName"
TPRoom @player *room
```
**Item Management:**
```ser
GiveItem @player KeycardO5
ClearInventory @player
```
**Health Management:**
```ser
SetHealth @player 100
SetMaxHealth @player 150
Heal @player 50
```
**Broadcast with Variables:**
```ser
Broadcast @all 5s "Player: {@player -> name}, Health: {@player -> health}"
```