Mission maker
Build the night, not the plumbing
Modules, items and ready-made systems first. Every behaviour here is configuration: hash maps, module attributes, callbacks. You do not need to read a single implementation file.
You build in Eden and write mission init files. This page covers everything the addon offers without touching SQF internals: modules, items, ready-made scripts, and the configuration values that shape them.
Install and load order
The suite ships as seven mod folders. Load FF_Core first, then the feature mods, then FF_Horror, which depends on all of them. Every client, headless client and the server need the same set. FF_Spectator is independent: missions that do not use spectator can leave it out entirely.
| Mod folder | PBOs | Needs | What it brings |
|---|---|---|---|
| @FF_Core | FF_core.pbo | CBA | Shared editor categories, module bases, UI contracts |
| @FF_Utils | FF_utils.pbo, FF_utils_assets.pbo | Core, CBA, ACE, ACE Arsenal | Wardrobe, circuits, faulty optics, documents, items, AI balancing, reconnect recovery |
| @FF_Cinematic | FF_cinematic.pbo | Core, CBA | Camera paths, gravity pulses |
| @FF_CBRN | FF_cbrn.pbo, FF_cbrn_assets.pbo | Core, CBA, ACE medical | Hazards, filters, SCBA, detector, decontamination |
| @FF_Zeus | FF_zeus_suite.pbo | Core, Utils, CBA | Curator footer, FPS monitoring, look orders, volunteer service, drone/music/ready-room/fault dialogs |
| @FF_Spectator | FF_spectator.pbo | Core, CBA, Cinematic | Opt-in spectator service: camera presets, command broker, UI, optional TFAR/ACRE2 |
| @FF_Horror | FF_horror.pbo | All of the above, CBA, ACE | Soul respawn, skulls, hordes, atmosphere, scares, themed items and media |
Restart Arma after replacing a PBO. ACE builds its item-category cache at startup and a loaded PBO is locked by the engine. For side-by-side testing use a separate release folder name.
ZEN is optional everywhere. Without it you lose the custom Zeus dialogs and the FF module menus, but you keep the curator footer, the public functions and every Eden module.
Modules, by component
All modules live under Systems > FF: …. Settings modules should be placed once; hazard zones, skulls, gravity pulses and circuit nodes may be repeated.
GUI Utils
Circuit producer / gate / consumer, faulty night vision equipment, wardrobe access, wardrobe settings.
CBRN CBRN
CBRN settings (once), CBRN hazard zone (repeatable, can start simulation on its own).
HOR Horror
Soul respawn settings, flying skull, gravity pulse.
Zeus dialogs (hordes, spawn settings, soul tools, atmosphere, scare, status rates) are covered in the Zeus guide. Full attribute lists are in Module reference.
Soul respawn
The template is FF_Horror_Soul. It is inactive until the mission lists it in description.ext.
respawn = "INSTANT";
respawnDelay = -1;
respawnOnStart = -1;
respawnTemplates[] = {"FF_Horror_Soul"};
Remove any old onPlayerKilled.sqf / onPlayerRespawn.sqf that also controls death. Two systems
writing the same unit is the most common cause of a soul that never finishes.
What players experience
- An orbiting camera without cinematic borders. Space resists, but the bar cannot pass 30% until a shield is applied.
- Another living player applies a soul shield capsule through ACE interaction on the corpse.
- The camera keeps orbiting while the orb rises, then a white fade hands control over.
- The orb must move away from its start, then reach an eligible object. Arrival plays a short scene and respawns the player beside it.
- There is no automatic timeout. Zeus Soul tools can recover a genuinely stuck player.
Mission overrides
FF_horror_config = createHashMapFromArray [
["respawnClasses", ["Land_Calvary_01_V1_F", "Land_Bozi_muka"]],
["searchRadius", 700],
["wakeKey", 57],
["firstDeathMusic", "MyFirstDeathTrack"],
["soulMusic", "MySoulTrack"],
["onFirstDeath", { params ["_body", "_cfg"]; /* scene */ }],
["onDestination", { params ["_dest", "_pos", "_cfg"]; }],
["onRespawn", { params ["_new", "_old", "_pos", "_cfg"]; }]
];
Every key is validated before use; invalid values fall back to defaults instead of breaking the sequence.
Gear, clues and keys
Item classes keep their original FF_Horror_* names even though ownership moved. Utils defines the 36
clue/part/key classes, CBRN the four filter classes, Horror the 13 themed items. Editor pickups are under
Props > FF: Horror in six subcategories.
_unit addMagazine "FF_Horror_CarKey_03";
if ("FF_Horror_CarKey_03" in magazines _unit) then { /* allow */ };
// any car key, without caring about the number
private _hasCarKey = ((magazines _unit) findIf {
_x isKindOf ["FF_Horror_CarKey", configFile >> "CfgMagazines"]
}) >= 0;
Full list and copyable class names: Item catalog.
Wardrobe
Place Wardrobe settings once to publish a policy, then use Wardrobe access modules or the script entry point. The default destination is the player's current position, so nothing assumes a specific map location.
// trigger or module script
[[], createHashMapFromArray [["arsenalMode", "mission"]]] call FF_utils_fnc_wardrobeOpen;
// position array, or marker name
[getMarkerPos "wardrobe_spawn"] spawn FF_utils_fnc_wardrobeOpen;
Inspectable documents
[_object, "images\clue.paa"] call FF_utils_fnc_initDocument;
Puzzles with circuits
Circuit nodes replace one-off door scripts. Synchronize nodes, give each a Node ID, and list upstream IDs
in the downstream node's Input node IDs. Direction comes from the IDs, so the same sync line can be reused.
- Producer — item in the player's inventory, item in a synchronized container, boolean/number on an object, or a mission variable.
- Gate — AND, OR, NOT, XOR.
- Consumer — ACE action on synchronized targets; on use it broadcasts a completion variable and optionally drives an animation source.
Circuits check conditions, they do not consume items. The server re-evaluates the circuit before writing state, so a desynced client cannot complete a puzzle it does not satisfy.
CBRN
Three ways to start, in order of precedence:
- Mission hash map
FF_cbrn_config(falls back toFF_horror_cbrnConfig) plus a local start call. - CBRN settings module (writes the CBRN configuration map).
- CBA setting
FF_horror_cbrnEnabled.
// initPlayerLocal.sqf
FF_cbrn_config = createHashMapFromArray [
["enabled", true],
["followWorldFog", true],
["fogTopASL", 60]
];
[] call FF_cbrn_fnc_cbrnRefresh;
Hazard zones are hash maps. They can also be published at runtime:
FF_horror_cbrnZones = [createHashMapFromArray [
["center", gasTank], ["radius", 180], ["innerRadius", 30],
["danger", 7], ["minASL", 0], ["maxASL", 100]
]];
Full key list: Configuration reference. Mask/filter mechanics: CBRN internals.
Atmosphere, scares, persistence
These three systems are plain entry scripts, deliberately not started by the addon. A mission opts in:
// initServer.sqf
[] execVM "\x\FF\addons\horror\systems\atmosphere\server.sqf";
[] execVM "\x\FF\addons\horror\systems\persistence\server.sqf";
[] execVM "\x\FF\addons\horror\systems\scare\server.sqf";
// initPlayerLocal.sqf
[] execVM "\x\FF\addons\horror\systems\atmosphere\client.sqf";
[] execVM "\x\FF\addons\horror\systems\persistence\client.sqf";
[] execVM "\x\FF\addons\horror\systems\scare\client.sqf";
Each script guards itself with a started flag, so double execution is harmless. Atmosphere auto-mood starts disabled and is enabled from Zeus when the briefing ends. Scare apparitions exist only where a Zeus creates one.
Spawning and AI
Flying arrivals and hordes are normally driven from Zeus, but the API is public and safe to call from mission scripts:
private _horde = ["create", AGLToASL [1000,1000,0], createHashMapFromArray [
["pool", "O_Soldier_F=5,O_Soldier_AR_F=1"],
["amount", 20], ["radius", 50], ["waves", 3],
["waveDelay", 30], ["maxAlive", 60], ["origin", "sky"]
]] call FF_horror_fnc_hordeControl;
["pause", _horde] call FF_horror_fnc_hordeControl;
["stop", _horde] call FF_horror_fnc_hordeControl;
AI ownership balancing is opt-in through the FF Utils / AI balancing CBA category and needs playable HC slots in
server.cfg to do anything useful.
Zombies: this addon uses WebKnight
FF Horror ships no zombies of its own. Every walking corpse in a FF Horror mission comes from the WebKnight (WBK) Zombies addon, which must be loaded alongside FF Horror on server and clients. FF Horror is the layer on top: it decides when, where and how many zombies arrive, it scores them by threat, and it feeds them into the atmosphere system.
What that means for you
- Add the WBK pack to the same mod list as FF Horror. Load order between the two does not matter.
- Spawn real WBK classes, not vanilla soldiers. A horde pool is a plain list of class names and weights:
"WBK_Crawler=3,WBK_Sprinter=1". - Leave the arrival filter at its default unless you know your class names. The default only accepts classes whose
name contains
zombie, which is what the WBK packs use. - Use All AI infantry only for deliberately mixed waves (soldiers + zombies). It is not the horror default.
Threat scoring you inherit
FF Horror knows the rough danger of the common WBK archetypes, so a horde of five Smashers is treated as far more serious than five Walkers: atmosphere tension, music choice and the Zeus footer all read that score. The built-in numbers (threat weight and the distance at which the entry becomes urgent) are:
| Archetype | Weight | Urgent from |
|---|---|---|
| Crawler, Walker | 0.15 | — |
| Triggerman / Shooter | 0.6 | — |
| Screamer | 0.5 | — |
| Shambler | 2 | — |
| Sprinter | 2.5 | — |
| Bloater / Boomer | 3 | 30 m (6 at 12 m) |
| Leaper | 6 | 15 m |
| Smasher | 16 | 30 m |
| Goliath / Goliaph | 50 | 45 m, combat on sight |
Unknown OPFOR classes get a neutral weight of 1 and are logged once, so a third-party zombie pack still
works — it just will not be scored as accurately.
Full list and reasoning: Lore → Bestiary.
Field utilities
Small Utils tools that need no modules. All shared-state calls are server-validated.
Blackout
private _r = [[[[1000,1000,0], 300]]] call FF_utils_fnc_blackout;
_r params ["_disabled", "_preserved"];
Disables terrain lamps outside the supplied safe circles. Zeus or server only.
Ammo provisioning
[_truck, units group player, 3] call FF_utils_fnc_prepareAmmo;
Tops a container up to a multiple of the operators' inventories. Server only.
Shared flashlights
Enable FF: Utils > Shared player flashlights so every client sees other players' beams. Off by default.
PASS beacons
Enable FF: Utils > PASS identification and casualty alarms for identification lights and automatic death/unconsciousness alarms on player bodies.
Loadout recovery
[createHashMapFromArray [["snapshotKey", "saved"]]
] call FF_utils_fnc_loadoutRecoveryStart;
Server-side snapshot that is copied to the respawned unit.
Inspectable documents
[_object, "images\clue.paa"] call FF_utils_fnc_initDocument;
UAV automation and live feeds
One airframe per registration: follow a player, escort a vehicle, run a patrol, hold, or watch an area and turn toward contacts. Nothing moves until the server registers the UAV.
// initServer.sqf
[_uav, createHashMapFromArray [
["route", [[100,200,0],[300,400,0]]],
["distance", 25], ["height", 5], ["interval", 3],
["enemySides", [east]]
]] call FF_utils_fnc_droneRegister;
[_uav, "operator", player] call FF_utils_fnc_droneAssign;
[_uav, "watch", true] call FF_utils_fnc_droneAssign;
// live feed, audience-aware
["survey", _uav, _objective, "players", true] call FF_utils_fnc_droneFeedSet;
- The UAV must already exist and be a UAV (
isUav = 1). Duplicate registrations and vehicles already flown by another registered UAV are refused. - Player-camera feeds are a per-client singleton — do not run a second live-feed controller in the same HUD slot.
- With ZEN, the curator gets Drone automation / Live feed modules so you never have to write the calls above.
Full keys, status values and cleanup rules: UAV internals.
Ready room
A briefing and assembly screen for the start of a session: literal briefing text, up to eight private questions, per-player readiness, an optional skip vote, team chat and an optional music bed. Headless clients are always excluded.
// initServer.sqf
[true, createHashMapFromArray [
["title", "Operation briefing"],
["briefing", ["Read the task, then mark ready."]],
["questions", ["What is your role?"]],
["countdown", 8], ["voteEnabled", true]
]] call FF_utils_fnc_readyRoomStart;
Release your own intro from a client callback — the room never moves a player or starts a scene by itself:
// initPlayerLocal.sqf
FF_utils_readyRoomClientConfig = createHashMapFromArray [
["onRelease", { params ["_skip", "_participants", "_participating", "_epoch"];
[] spawn my_fnc_playIntro; }]
];
Policy keys, vote maths and privacy rules: Ready room internals.
Weapon faults
Deliberate firearm malfunctions that only a configured specialist can clear, plus an expanding pulse that jams weapons over an area. Nothing is random: the service only reacts to your calls.
// initServer.sqf
[true, createHashMapFromArray [
["duration", 10], ["traits", ["engineer"]]
]] call FF_utils_fnc_weaponFaultStart;
// later, from a trigger or a scripted event
[getPosASL _emitter, serverTime, 500, 50, false] call FF_utils_fnc_weaponFaultPulse;
Repairs are server-owned timed reservations, so two medics cannot both "finish" the same jam. ACE's own jams stay independent; ordinary ACE jam clearing does not remove a native fault.
Power, solar and terminals
Three building blocks for bases and installations. All are server-registered, all are opt-in, and none of them assumes object names or coordinates.
Transfer power
["campPower", _generator, _panel,
createHashMapFromArray [
["limits", [0.1, 0.65, 0.9, 0.98]]
]] call FF_utils_fnc_powerRegister;
["campPower", true]
call FF_utils_fnc_powerSetInput;
Charge window with a safe RUN band and a trip latch. Players interact physically; supply is server-owned.
Solar commissioning
["array", _console,
createHashMapFromArray [
["dwell", 6],
["access", {params ["_u"];
(_u getUnitTrait "engineer")}]
]] call FF_utils_fnc_solarRegister;
XOR pairing, active-low flags and coupled voltage dials, with a shipped player panel.
Terminals
["station", _terminal,
createHashMapFromArray [
["power", {["campPower"]
call FF_utils_fnc_powerHasSupply}],
["services", [["lights", "Tower",
"lights", [_lamp]]]]
]] call FF_utils_fnc_terminalRegister;
Case/boot phases, credentials, media-gated archives and shared channels. Server half only — no browser yet.
Wiring them together
A terminal reads powerHasSupply, a transfer site reads the solar puzzle with
solarIsSolved, and onSupply can power a registered UAV. No component links to another
implicitly.
Details: Power, Solar, Terminals.
Mission music and overlays
If you want a soundtrack that stays in sync for everyone without writing a playback loop, register a director on the server and let each client opt into the follower.
// initServer.sqf
["operation", createHashMapFromArray [
["tracks", [["calm1", "Calm", "myCalmTrack", 120, 0.25]]],
["pools", [["calm", ["calm1"]]]]
]] call FF_cinematic_fnc_musicDirectorStart;
["operation", "select", ["calm", "quiet", 0]] call FF_cinematic_fnc_musicDirectorControl;
// initPlayerLocal.sqf
["operationPlayback", createHashMapFromArray [
["source", {["operation"] call FF_cinematic_fnc_musicDirectorState}]
]] call FF_cinematic_fnc_musicStart;
For a one-off cue, play it yourself and claim the channel first with FF_cinematic_fnc_musicHold so the native
track yields instead of fighting you.
Playback and overlays · Director.
Gravity traps
A prop that fires one expanding gravity wave when a player opens it or when it is destroyed.
// initServer.sqf
[_crate, createHashMapFromArray [
["radius", 50], ["duration", 1]
]] call FF_horror_fnc_gravityTrapRegister;
The default hit effect faults both firearms of every player the wave reaches. Supply onHit when you want a
different consequence: it runs on the server once per reached player.
Briefing screens and scene staging
Presentation services for the opening of a mission. All of them are opt-in and clean themselves up.
Briefing screen
["briefing", _screen, [
["Situation", "Text...", "text"],
["Team", "", "ready"]
], createHashMapFromArray [
["title", "Operation briefing"]
]] call FF_cinematic_fnc_briefingRegister;
Synchronized slides plus a live readiness roster; late joiners see the current slide.
Face cameras
private _plan = [_cast, _marks, _blocks]
call FF_cinematic_fnc_facecamPlan;
[_plan, serverTime]
call FF_cinematic_fnc_facecamStart;
Weighted appearances per scene block, with timed restoration of body and seat.
Image cards
[[["images\a.paa", 4, 1, 1],
["images\b.paa", 3, 1, 1]], {}, true]
call FF_cinematic_fnc_splashStart;
Full-screen cards with per-card hold and fades; can hold the music director.
Drive a vehicle into a scene
[_truck, [400, 200, 0], "Arrive"]
call FF_cinematic_fnc_driveScene;
Orders the vehicle's AI crew to a destination without touching player control.
Spectator
A separate mod, off until you switch it on. The server decides who can watch, the client gets thirteen camera presets, six overlay layers, night vision control and optional personal-radio sync.
// initServer.sqf and initPlayerLocal.sqf
[true, "auto", createHashMapFromArray [["slots", [_viewerUnit]]]]
call FF_spectator_fnc_serviceStart;
Viewers are an explicit VirtualSpectator_F, an Eden unit named spectator, or a unit in
slots; everyone else stays a controller or an ordinary player. Alias entry points share the same implementation.
Full detail: Spectator internals.
Custom Zeus footer fields
// initServer.sqf
["objectives", "Objectives", { missionNamespace getVariable ["done", 0] }, 2, "server"]
call FF_zeus_fnc_registerZeusFooterStatus;
["objectives", "server"] call FF_zeus_fnc_unregisterZeusFooterStatus;
Register on the machine that owns the value: server fields are broadcast, local fields are viewer-only. Horror's
FF_horror_fnc_registerZeusFooterStatus is also available and calls the Zeus registry.
Naming contract
| Name | Rule |
|---|---|
FF_Horror_* item and pickup class names | Public API. Mission scripts, loadouts and editor placements use them. |
FF_horror_fnc_* public functions | Public API. Each one calls the implementation owned by its component. |
| Editor module class names and attributes | Public API. Eden files store them by name. |
State keys such as FF_horror_cbrn*, FFPP_*, FFATM_* | Public API. Missions read and write them directly. |
| CBA setting identifiers | Public API. CBA presets store them by name. |